import { p as propTypesExports, d as createTheme, j as jsxRuntimeExports, e as utils$7, g as styled, B as Box, i as Button, A as AccountCircleIcon, C as CalendarIcon, T as TeacherIcon, k as BusinessIcon, G as GraphIcon, m as ClassIcon, n as Tooltip, I as IconButton, q as ArrowBackIcon, H as HistoryIcon, r as ArrowForwardIcon, M as Menu$1, t as MenuItem, L as ListItemIcon, v as ListItemText, S as StudentIcon, E as ExpandMoreIcon, w as useTheme, x as AppBar, y as Toolbar$1, z as Typography, D as TLDrawDevIcon, F as DevToolsIcon, J as Divider, K as MultiplayerIcon, N as ExamIcon, O as ExamMarkerIcon, P as SettingsIcon, Q as SearchIcon, R as AdminIcon, U as LogoutIcon, V as LoginIcon, W as Alert, X as TextField, Y as Container, Z as Stack, _ as Paper, $ as CircularProgress, a0 as ButtonGroup, a1 as List, a2 as ListItem, a3 as Tabs, a4 as useMediaQuery, a5 as ThemeProvider, a6 as Tab, a7 as PushPinIcon, a8 as PushPinOutlinedIcon, a9 as ShapesIcon, aa as NodeIcon, ab as NavigationIcon, ac as YouTubeIcon, ad as SlidesIcon, ae as Snackbar } from './vendor-mui.js'; import { r as reactExports, e as getAugmentedNamespace, a as reactDomExports, g as getDefaultExportFromCjs, u as useNavigate, b as React$2, f as useLocation, h as useSearchParams, i as Routes, j as Route, B as BrowserRouter } from './vendor-react.js'; import { c as createShapeId, g as getSnapshot, l as loadSnapshot, B as BaseBoxShapeUtil, H as HTMLContainer, t as toDomPrecision, a as arrayOf, s as string, o as object$1, n as number, b as boolean, D as DefaultSizeStyle, d as DefaultDashStyle, e as DefaultColorStyle, f as clamp$1, h as getIndexBetween, T as TldrawUiDialogHeader, i as TldrawUiDialogTitle, j as TldrawUiDialogCloseButton, k as TldrawUiDialogBody, m as TldrawUiDialogFooter, p as TldrawUiButton, q as TldrawUiButtonLabel, u as useEditor, r as useDialogs, R as Rectangle2d, v as optional, w as DEFAULT_EMBED_DEFINITIONS, x as BindingUtil, V as Vec, y as createShapePropsMigrationSequence, z as createShapePropsMigrationIds, A as createTLSchema, C as defaultShapeSchemas, E as defaultBindingSchemas, F as createTLSchemaFromUtils, G as defaultBindingUtils, I as defaultShapeUtils, J as createTLStore, K as DefaultToolbar, L as TldrawUiMenuItem, M as DefaultToolbarContent, N as DefaultHelperButtons, O as DefaultHelperButtonsContent, P as DefaultNavigationPanel, Q as DefaultStylePanel, S as DefaultStylePanelContent, U as useRelevantStyles, W as DefaultZoomMenu, X as DefaultZoomMenuContent, Y as useTools, Z as DefaultKeyboardShortcutsDialog, _ as DefaultKeyboardShortcutsDialogContent, $ as DefaultContextMenu, a0 as DefaultContextMenuContent, a1 as DefaultDebugMenu, a2 as DefaultDebugMenuContent, a3 as useToasts, a4 as DefaultActionsMenu, a5 as DefaultActionsMenuContent, a6 as createBindingId, a7 as atom, a8 as useValue, a9 as exportToBlob, aa as Box$1, ab as computed, ac as BaseBoxShapeTool, ad as StateNode, ae as useTldrawUser, af as Tldraw, ag as DEFAULT_SUPPORT_VIDEO_TYPES, ah as DEFAULT_SUPPORTED_IMAGE_TYPES, ai as objectMapEntries, aj as objectMapValues, ak as isEqual, al as react, am as uniqueId, an as transact, ao as reverseRecordsDiff, ap as exhaustiveSwitchError, aq as fpsThrottle, ar as squashRecordDiffs, as as warnOnce, at as assert, au as registerTldrawLibraryVersion, av as useRefState, aw as useTLSchemaFromUtils, ax as useShallowObjectIdentity, ay as useAtom, az as isSignal, aA as getUserPreferences, aB as defaultUserPreferences, aC as TAB_ID, aD as createPresenceStateDerivation, aE as AssetRecordType, aF as getHashForString, aG as client } from './vendor-tldraw.js'; import { c as createClient, a as axios$1, b as create$1, A as AxiosError, _ as __vitePreload } from './vendor-utils.js'; true&&(function polyfill() { const relList = document.createElement("link").relList; if (relList && relList.supports && relList.supports("modulepreload")) { return; } for (const link of document.querySelectorAll('link[rel="modulepreload"]')) { processPreload(link); } new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type !== "childList") { continue; } for (const node of mutation.addedNodes) { if (node.tagName === "LINK" && node.rel === "modulepreload") processPreload(node); } } }).observe(document, { childList: true, subtree: true }); function getFetchOpts(link) { const fetchOpts = {}; if (link.integrity) fetchOpts.integrity = link.integrity; if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy; if (link.crossOrigin === "use-credentials") fetchOpts.credentials = "include"; else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit"; else fetchOpts.credentials = "same-origin"; return fetchOpts; } function processPreload(link) { if (link.ep) return; link.ep = true; const fetchOpts = getFetchOpts(link); fetch(link.href, fetchOpts); } }()); const LOG_LEVELS = { error: 0, // Always shown if enabled warn: 1, // Shows warns and errors info: 2, // Shows info, warns, and errors debug: 3, // Shows debug and above trace: 4 // Shows everything }; class DebugLogger { config = { enabled: true, level: "debug", categories: [ "system", "navigation", "presentation", "selection", "camera", "binding", "shape", "tldraw-service" ] }; setConfig(config) { this.config = { ...this.config, ...config }; } shouldLog(level, category) { return this.config.enabled && LOG_LEVELS[level] <= LOG_LEVELS[this.config.level] && this.config.categories.includes(category); } log(level, category, message, data) { if (!this.shouldLog(level, category)) { return; } const levelEmojis = { error: "πŸ”΄", // Red circle for errors warn: "⚠️", // Warning symbol info: "ℹ️", // Information symbol debug: "πŸ”§", // Wrench for debug trace: "πŸ”" // Magnifying glass for trace }; const prefix = `${levelEmojis[level]} [${category}]`; switch (level) { case "error": if (data) { console.error(`${prefix} ${message}`, data); } else { console.error(`${prefix} ${message}`); } break; case "warn": if (data) { console.warn(`${prefix} ${message}`, data); } else { console.warn(`${prefix} ${message}`); } break; case "info": if (data) { console.info(`${prefix} ${message}`, data); } else { console.info(`${prefix} ${message}`); } break; case "debug": if (data) { console.debug(`${prefix} ${message}`, data); } else { console.debug(`${prefix} ${message}`); } break; case "trace": if (data) { console.trace(`${prefix} ${message}`, data); } else { console.trace(`${prefix} ${message}`); } break; } } // Convenience methods error(category, message, data) { this.log("error", category, message, data); } warn(category, message, data) { this.log("warn", category, message, data); } info(category, message, data) { this.log("info", category, message, data); } debug(category, message, data) { this.log("debug", category, message, data); } trace(category, message, data) { this.log("trace", category, message, data); } } const logger = new DebugLogger(); logger.setConfig({ enabled: true, level: "debug", categories: [ "app", "header", "routing", "neo4j-context", "auth-context", "auth-service", "state-management", "local-storage", "axios", "system", "navigation", "calendar", "presentation", "selection", "camera", "binding", "shape", "tldraw-service", "tldraw-events", "signup-page", "timetable-service", "dev-page", "super-admin-auth-route", "admin-page", "storage-service", "user-context", "login-form", "super-admin-section", "routes", "neo4j-service", "supabase-client", "user-page", "site-page", "auth-page", "email-signup-form", "supabase-profile-service", "multiplayer-page", "snapshot-service", "sync-service", "slides-panel", "local-store-service", "shared-store-service", "single-player-page", "user-toolbar", "registration-service", "graph-service", "graph-shape", "calendar-shape", "snapshot-toolbar", "graphStateUtil", "baseNodeShapeUtil", "school-service", "microphone-state-tool", "store-service", "morphic-page", "not-found", "share-handler", "transcription-service", "slideshow-helpers", "slide-shape", "graph-panel", "cc-user-node-shape-util", "cc-base-shape-util", "node-canvas", "navigation-service", "autosave", "cc-exam-marker", "cc-search", "cc-web-browser", "neo-user-context", "neo-institute-context", "cc-node-snapshot-panel", "user-neo-db", "navigation-queue-service", "editor-state", "neo-shape-service", // Add new navigation categories "navigation-context", "navigation-history", "navigation-ui", "navigation-store", "navigation-queue", "navigation-state", "context-switch", "history-management", "node-navigation", "navigation-panel", "auth", "school-context", "database-name-service", "tldraw", "websocket", "app", "auth-service", "storage-service", "routing", "auth-service", "user-context", "neo-user-context", "neo-institute-context" ] }); var lib = {exports: {}}; var Modal$2 = {}; var ModalPortal = {exports: {}}; var focusManager = {}; var tabbable = {exports: {}}; (function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.default = findTabbableDescendants; /*! * Adapted from jQuery UI core * * http://jqueryui.com * * Copyright 2014 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/category/ui-core/ */ var DISPLAY_NONE = "none"; var DISPLAY_CONTENTS = "contents"; // match the whole word to prevent fuzzy searching var tabbableNode = /^(input|select|textarea|button|object|iframe)$/; function isNotOverflowing(element, style) { return style.getPropertyValue("overflow") !== "visible" || // if 'overflow: visible' set, check if there is actually any overflow element.scrollWidth <= 0 && element.scrollHeight <= 0; } function hidesContents(element) { var zeroSize = element.offsetWidth <= 0 && element.offsetHeight <= 0; // If the node is empty, this is good enough if (zeroSize && !element.innerHTML) return true; try { // Otherwise we need to check some styles var style = window.getComputedStyle(element); var displayValue = style.getPropertyValue("display"); return zeroSize ? displayValue !== DISPLAY_CONTENTS && isNotOverflowing(element, style) : displayValue === DISPLAY_NONE; } catch (exception) { // eslint-disable-next-line no-console console.warn("Failed to inspect element style"); return false; } } function visible(element) { var parentElement = element; var rootNode = element.getRootNode && element.getRootNode(); while (parentElement) { if (parentElement === document.body) break; // if we are not hidden yet, skip to checking outside the Web Component if (rootNode && parentElement === rootNode) parentElement = rootNode.host.parentNode; if (hidesContents(parentElement)) return false; parentElement = parentElement.parentNode; } return true; } function focusable(element, isTabIndexNotNaN) { var nodeName = element.nodeName.toLowerCase(); var res = tabbableNode.test(nodeName) && !element.disabled || (nodeName === "a" ? element.href || isTabIndexNotNaN : isTabIndexNotNaN); return res && visible(element); } function tabbable(element) { var tabIndex = element.getAttribute("tabindex"); if (tabIndex === null) tabIndex = undefined; var isTabIndexNaN = isNaN(tabIndex); return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN); } function findTabbableDescendants(element) { var descendants = [].slice.call(element.querySelectorAll("*"), 0).reduce(function (finished, el) { return finished.concat(!el.shadowRoot ? [el] : findTabbableDescendants(el.shadowRoot)); }, []); return descendants.filter(tabbable); } module.exports = exports["default"]; } (tabbable, tabbable.exports)); var tabbableExports = tabbable.exports; Object.defineProperty(focusManager, "__esModule", { value: true }); focusManager.resetState = resetState$4; focusManager.log = log$4; focusManager.handleBlur = handleBlur; focusManager.handleFocus = handleFocus; focusManager.markForFocusLater = markForFocusLater; focusManager.returnFocus = returnFocus; focusManager.popWithoutFocus = popWithoutFocus; focusManager.setupScopedFocus = setupScopedFocus; focusManager.teardownScopedFocus = teardownScopedFocus; var _tabbable = tabbableExports; var _tabbable2 = _interopRequireDefault$i(_tabbable); function _interopRequireDefault$i(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var focusLaterElements = []; var modalElement = null; var needToFocus = false; function resetState$4() { focusLaterElements = []; } function log$4() { } function handleBlur() { needToFocus = true; } function handleFocus() { if (needToFocus) { needToFocus = false; if (!modalElement) { return; } setTimeout(function() { if (modalElement.contains(document.activeElement)) { return; } var el = (0, _tabbable2.default)(modalElement)[0] || modalElement; el.focus(); }, 0); } } function markForFocusLater() { focusLaterElements.push(document.activeElement); } function returnFocus() { var preventScroll = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false; var toFocus = null; try { if (focusLaterElements.length !== 0) { toFocus = focusLaterElements.pop(); toFocus.focus({ preventScroll }); } return; } catch (e) { console.warn(["You tried to return focus to", toFocus, "but it is not in the DOM anymore"].join(" ")); } } function popWithoutFocus() { focusLaterElements.length > 0 && focusLaterElements.pop(); } function setupScopedFocus(element) { modalElement = element; if (window.addEventListener) { window.addEventListener("blur", handleBlur, false); document.addEventListener("focus", handleFocus, true); } else { window.attachEvent("onBlur", handleBlur); document.attachEvent("onFocus", handleFocus); } } function teardownScopedFocus() { modalElement = null; if (window.addEventListener) { window.removeEventListener("blur", handleBlur); document.removeEventListener("focus", handleFocus); } else { window.detachEvent("onBlur", handleBlur); document.detachEvent("onFocus", handleFocus); } } var scopeTab = {exports: {}}; (function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.default = scopeTab; var _tabbable = tabbableExports; var _tabbable2 = _interopRequireDefault(_tabbable); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function getActiveElement() { var el = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document; return el.activeElement.shadowRoot ? getActiveElement(el.activeElement.shadowRoot) : el.activeElement; } function scopeTab(node, event) { var tabbable = (0, _tabbable2.default)(node); if (!tabbable.length) { // Do nothing, since there are no elements that can receive focus. event.preventDefault(); return; } var target = void 0; var shiftKey = event.shiftKey; var head = tabbable[0]; var tail = tabbable[tabbable.length - 1]; var activeElement = getActiveElement(); // proceed with default browser behavior on tab. // Focus on last element on shift + tab. if (node === activeElement) { if (!shiftKey) return; target = tail; } if (tail === activeElement && !shiftKey) { target = head; } if (head === activeElement && shiftKey) { target = tail; } if (target) { event.preventDefault(); target.focus(); return; } // Safari radio issue. // // Safari does not move the focus to the radio button, // so we need to force it to really walk through all elements. // // This is very error prone, since we are trying to guess // if it is a safari browser from the first occurence between // chrome or safari. // // The chrome user agent contains the first ocurrence // as the 'chrome/version' and later the 'safari/version'. var checkSafari = /(\bChrome\b|\bSafari\b)\//.exec(navigator.userAgent); var isSafariDesktop = checkSafari != null && checkSafari[1] != "Chrome" && /\biPod\b|\biPad\b/g.exec(navigator.userAgent) == null; // If we are not in safari desktop, let the browser control // the focus if (!isSafariDesktop) return; var x = tabbable.indexOf(activeElement); if (x > -1) { x += shiftKey ? -1 : 1; } target = tabbable[x]; // If the tabbable element does not exist, // focus head/tail based on shiftKey if (typeof target === "undefined") { event.preventDefault(); target = shiftKey ? tail : head; target.focus(); return; } event.preventDefault(); target.focus(); } module.exports = exports["default"]; } (scopeTab, scopeTab.exports)); var scopeTabExports = scopeTab.exports; var ariaAppHider$1 = {}; var warning = function() { }; var warning_1 = warning; var safeHTMLElement = {}; var exenv = {exports: {}}; /*! Copyright (c) 2015 Jed Watson. Based on code that is Copyright 2013-2015, Facebook, Inc. All rights reserved. */ (function (module) { /* global define */ (function () { var canUseDOM = !!( typeof window !== 'undefined' && window.document && window.document.createElement ); var ExecutionEnvironment = { canUseDOM: canUseDOM, canUseWorkers: typeof Worker !== 'undefined', canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent), canUseViewport: canUseDOM && !!window.screen }; if (module.exports) { module.exports = ExecutionEnvironment; } else { window.ExecutionEnvironment = ExecutionEnvironment; } }()); } (exenv)); var exenvExports = exenv.exports; Object.defineProperty(safeHTMLElement, "__esModule", { value: true }); safeHTMLElement.canUseDOM = safeHTMLElement.SafeNodeList = safeHTMLElement.SafeHTMLCollection = undefined; var _exenv = exenvExports; var _exenv2 = _interopRequireDefault$h(_exenv); function _interopRequireDefault$h(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var EE = _exenv2.default; var SafeHTMLElement = EE.canUseDOM ? window.HTMLElement : {}; safeHTMLElement.SafeHTMLCollection = EE.canUseDOM ? window.HTMLCollection : {}; safeHTMLElement.SafeNodeList = EE.canUseDOM ? window.NodeList : {}; safeHTMLElement.canUseDOM = EE.canUseDOM; safeHTMLElement.default = SafeHTMLElement; Object.defineProperty(ariaAppHider$1, "__esModule", { value: true }); ariaAppHider$1.resetState = resetState$3; ariaAppHider$1.log = log$3; ariaAppHider$1.assertNodeList = assertNodeList; ariaAppHider$1.setElement = setElement; ariaAppHider$1.validateElement = validateElement; ariaAppHider$1.hide = hide; ariaAppHider$1.show = show; ariaAppHider$1.documentNotReadyOrSSRTesting = documentNotReadyOrSSRTesting; var _warning = warning_1; var _warning2 = _interopRequireDefault$g(_warning); var _safeHTMLElement$1 = safeHTMLElement; function _interopRequireDefault$g(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var globalElement = null; function resetState$3() { if (globalElement) { if (globalElement.removeAttribute) { globalElement.removeAttribute("aria-hidden"); } else if (globalElement.length != null) { globalElement.forEach(function(element) { return element.removeAttribute("aria-hidden"); }); } else { document.querySelectorAll(globalElement).forEach(function(element) { return element.removeAttribute("aria-hidden"); }); } } globalElement = null; } function log$3() { } function assertNodeList(nodeList, selector) { if (!nodeList || !nodeList.length) { throw new Error("react-modal: No elements were found for selector " + selector + "."); } } function setElement(element) { var useElement = element; if (typeof useElement === "string" && _safeHTMLElement$1.canUseDOM) { var el = document.querySelectorAll(useElement); assertNodeList(el, useElement); useElement = el; } globalElement = useElement || globalElement; return globalElement; } function validateElement(appElement) { var el = appElement || globalElement; if (el) { return Array.isArray(el) || el instanceof HTMLCollection || el instanceof NodeList ? el : [el]; } else { (0, _warning2.default)(false, ["react-modal: App element is not defined.", "Please use `Modal.setAppElement(el)` or set `appElement={el}`.", "This is needed so screen readers don't see main content", "when modal is opened. It is not recommended, but you can opt-out", "by setting `ariaHideApp={false}`."].join(" ")); return []; } } function hide(appElement) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = void 0; try { for (var _iterator = validateElement(appElement)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var el = _step.value; el.setAttribute("aria-hidden", "true"); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } function show(appElement) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = void 0; try { for (var _iterator2 = validateElement(appElement)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var el = _step2.value; el.removeAttribute("aria-hidden"); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } function documentNotReadyOrSSRTesting() { globalElement = null; } var classList$1 = {}; Object.defineProperty(classList$1, "__esModule", { value: true }); classList$1.resetState = resetState$2; classList$1.log = log$2; var htmlClassList = {}; var docBodyClassList = {}; function removeClass(at, cls) { at.classList.remove(cls); } function resetState$2() { var htmlElement = document.getElementsByTagName("html")[0]; for (var cls in htmlClassList) { removeClass(htmlElement, htmlClassList[cls]); } var body = document.body; for (var _cls in docBodyClassList) { removeClass(body, docBodyClassList[_cls]); } htmlClassList = {}; docBodyClassList = {}; } function log$2() { } var incrementReference = function incrementReference2(poll, className) { if (!poll[className]) { poll[className] = 0; } poll[className] += 1; return className; }; var decrementReference = function decrementReference2(poll, className) { if (poll[className]) { poll[className] -= 1; } return className; }; var trackClass = function trackClass2(classListRef, poll, classes) { classes.forEach(function(className) { incrementReference(poll, className); classListRef.add(className); }); }; var untrackClass = function untrackClass2(classListRef, poll, classes) { classes.forEach(function(className) { decrementReference(poll, className); poll[className] === 0 && classListRef.remove(className); }); }; classList$1.add = function add2(element, classString) { return trackClass(element.classList, element.nodeName.toLowerCase() == "html" ? htmlClassList : docBodyClassList, classString.split(" ")); }; classList$1.remove = function remove2(element, classString) { return untrackClass(element.classList, element.nodeName.toLowerCase() == "html" ? htmlClassList : docBodyClassList, classString.split(" ")); }; var portalOpenInstances$1 = {}; Object.defineProperty(portalOpenInstances$1, "__esModule", { value: true }); portalOpenInstances$1.log = log$1; portalOpenInstances$1.resetState = resetState$1; function _classCallCheck$1(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var PortalOpenInstances = function PortalOpenInstances2() { var _this = this; _classCallCheck$1(this, PortalOpenInstances2); this.register = function(openInstance) { if (_this.openInstances.indexOf(openInstance) !== -1) { return; } _this.openInstances.push(openInstance); _this.emit("register"); }; this.deregister = function(openInstance) { var index = _this.openInstances.indexOf(openInstance); if (index === -1) { return; } _this.openInstances.splice(index, 1); _this.emit("deregister"); }; this.subscribe = function(callback) { _this.subscribers.push(callback); }; this.emit = function(eventType) { _this.subscribers.forEach(function(subscriber) { return subscriber( eventType, // shallow copy to avoid accidental mutation _this.openInstances.slice() ); }); }; this.openInstances = []; this.subscribers = []; }; var portalOpenInstances = new PortalOpenInstances(); function log$1() { console.log("portalOpenInstances ----------"); console.log(portalOpenInstances.openInstances.length); portalOpenInstances.openInstances.forEach(function(p) { return console.log(p); }); console.log("end portalOpenInstances ----------"); } function resetState$1() { portalOpenInstances = new PortalOpenInstances(); } portalOpenInstances$1.default = portalOpenInstances; var bodyTrap$1 = {}; Object.defineProperty(bodyTrap$1, "__esModule", { value: true }); bodyTrap$1.resetState = resetState; bodyTrap$1.log = log; var _portalOpenInstances = portalOpenInstances$1; var _portalOpenInstances2 = _interopRequireDefault$f(_portalOpenInstances); function _interopRequireDefault$f(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var before = void 0, after = void 0, instances = []; function resetState() { var _arr = [before, after]; for (var _i = 0; _i < _arr.length; _i++) { var item = _arr[_i]; if (!item) continue; item.parentNode && item.parentNode.removeChild(item); } before = after = null; instances = []; } function log() { console.log("bodyTrap ----------"); console.log(instances.length); var _arr2 = [before, after]; for (var _i2 = 0; _i2 < _arr2.length; _i2++) { var item = _arr2[_i2]; var check = item || {}; console.log(check.nodeName, check.className, check.id); } console.log("edn bodyTrap ----------"); } function focusContent() { if (instances.length === 0) { return; } instances[instances.length - 1].focusContent(); } function bodyTrap(eventType, openInstances) { if (!before && !after) { before = document.createElement("div"); before.setAttribute("data-react-modal-body-trap", ""); before.style.position = "absolute"; before.style.opacity = "0"; before.setAttribute("tabindex", "0"); before.addEventListener("focus", focusContent); after = before.cloneNode(); after.addEventListener("focus", focusContent); } instances = openInstances; if (instances.length > 0) { if (document.body.firstChild !== before) { document.body.insertBefore(before, document.body.firstChild); } if (document.body.lastChild !== after) { document.body.appendChild(after); } } else { if (before.parentElement) { before.parentElement.removeChild(before); } if (after.parentElement) { after.parentElement.removeChild(after); } } } _portalOpenInstances2.default.subscribe(bodyTrap); (function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { return typeof obj; } : function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = /* @__PURE__ */ function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = reactExports; var _propTypes = propTypesExports; var _propTypes2 = _interopRequireDefault(_propTypes); var _focusManager = focusManager; var focusManager$1 = _interopRequireWildcard(_focusManager); var _scopeTab = scopeTabExports; var _scopeTab2 = _interopRequireDefault(_scopeTab); var _ariaAppHider = ariaAppHider$1; var ariaAppHider = _interopRequireWildcard(_ariaAppHider); var _classList = classList$1; var classList = _interopRequireWildcard(_classList); var _safeHTMLElement = safeHTMLElement; var _safeHTMLElement2 = _interopRequireDefault(_safeHTMLElement); var _portalOpenInstances = portalOpenInstances$1; var _portalOpenInstances2 = _interopRequireDefault(_portalOpenInstances); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var CLASS_NAMES = { overlay: "ReactModal__Overlay", content: "ReactModal__Content" }; var isTabKey = function isTabKey2(event) { return event.code === "Tab" || event.keyCode === 9; }; var isEscKey = function isEscKey2(event) { return event.code === "Escape" || event.keyCode === 27; }; var ariaHiddenInstances = 0; var ModalPortal = function(_Component) { _inherits(ModalPortal2, _Component); function ModalPortal2(props) { _classCallCheck(this, ModalPortal2); var _this = _possibleConstructorReturn(this, (ModalPortal2.__proto__ || Object.getPrototypeOf(ModalPortal2)).call(this, props)); _this.setOverlayRef = function(overlay) { _this.overlay = overlay; _this.props.overlayRef && _this.props.overlayRef(overlay); }; _this.setContentRef = function(content) { _this.content = content; _this.props.contentRef && _this.props.contentRef(content); }; _this.afterClose = function() { var _this$props = _this.props, appElement = _this$props.appElement, ariaHideApp = _this$props.ariaHideApp, htmlOpenClassName = _this$props.htmlOpenClassName, bodyOpenClassName = _this$props.bodyOpenClassName, parentSelector = _this$props.parentSelector; var parentDocument = parentSelector && parentSelector().ownerDocument || document; bodyOpenClassName && classList.remove(parentDocument.body, bodyOpenClassName); htmlOpenClassName && classList.remove(parentDocument.getElementsByTagName("html")[0], htmlOpenClassName); if (ariaHideApp && ariaHiddenInstances > 0) { ariaHiddenInstances -= 1; if (ariaHiddenInstances === 0) { ariaAppHider.show(appElement); } } if (_this.props.shouldFocusAfterRender) { if (_this.props.shouldReturnFocusAfterClose) { focusManager$1.returnFocus(_this.props.preventScroll); focusManager$1.teardownScopedFocus(); } else { focusManager$1.popWithoutFocus(); } } if (_this.props.onAfterClose) { _this.props.onAfterClose(); } _portalOpenInstances2.default.deregister(_this); }; _this.open = function() { _this.beforeOpen(); if (_this.state.afterOpen && _this.state.beforeClose) { clearTimeout(_this.closeTimer); _this.setState({ beforeClose: false }); } else { if (_this.props.shouldFocusAfterRender) { focusManager$1.setupScopedFocus(_this.node); focusManager$1.markForFocusLater(); } _this.setState({ isOpen: true }, function() { _this.openAnimationFrame = requestAnimationFrame(function() { _this.setState({ afterOpen: true }); if (_this.props.isOpen && _this.props.onAfterOpen) { _this.props.onAfterOpen({ overlayEl: _this.overlay, contentEl: _this.content }); } }); }); } }; _this.close = function() { if (_this.props.closeTimeoutMS > 0) { _this.closeWithTimeout(); } else { _this.closeWithoutTimeout(); } }; _this.focusContent = function() { return _this.content && !_this.contentHasFocus() && _this.content.focus({ preventScroll: true }); }; _this.closeWithTimeout = function() { var closesAt = Date.now() + _this.props.closeTimeoutMS; _this.setState({ beforeClose: true, closesAt }, function() { _this.closeTimer = setTimeout(_this.closeWithoutTimeout, _this.state.closesAt - Date.now()); }); }; _this.closeWithoutTimeout = function() { _this.setState({ beforeClose: false, isOpen: false, afterOpen: false, closesAt: null }, _this.afterClose); }; _this.handleKeyDown = function(event) { if (isTabKey(event)) { (0, _scopeTab2.default)(_this.content, event); } if (_this.props.shouldCloseOnEsc && isEscKey(event)) { event.stopPropagation(); _this.requestClose(event); } }; _this.handleOverlayOnClick = function(event) { if (_this.shouldClose === null) { _this.shouldClose = true; } if (_this.shouldClose && _this.props.shouldCloseOnOverlayClick) { if (_this.ownerHandlesClose()) { _this.requestClose(event); } else { _this.focusContent(); } } _this.shouldClose = null; }; _this.handleContentOnMouseUp = function() { _this.shouldClose = false; }; _this.handleOverlayOnMouseDown = function(event) { if (!_this.props.shouldCloseOnOverlayClick && event.target == _this.overlay) { event.preventDefault(); } }; _this.handleContentOnClick = function() { _this.shouldClose = false; }; _this.handleContentOnMouseDown = function() { _this.shouldClose = false; }; _this.requestClose = function(event) { return _this.ownerHandlesClose() && _this.props.onRequestClose(event); }; _this.ownerHandlesClose = function() { return _this.props.onRequestClose; }; _this.shouldBeClosed = function() { return !_this.state.isOpen && !_this.state.beforeClose; }; _this.contentHasFocus = function() { return document.activeElement === _this.content || _this.content.contains(document.activeElement); }; _this.buildClassName = function(which, additional) { var classNames = (typeof additional === "undefined" ? "undefined" : _typeof(additional)) === "object" ? additional : { base: CLASS_NAMES[which], afterOpen: CLASS_NAMES[which] + "--after-open", beforeClose: CLASS_NAMES[which] + "--before-close" }; var className = classNames.base; if (_this.state.afterOpen) { className = className + " " + classNames.afterOpen; } if (_this.state.beforeClose) { className = className + " " + classNames.beforeClose; } return typeof additional === "string" && additional ? className + " " + additional : className; }; _this.attributesFromObject = function(prefix, items) { return Object.keys(items).reduce(function(acc, name) { acc[prefix + "-" + name] = items[name]; return acc; }, {}); }; _this.state = { afterOpen: false, beforeClose: false }; _this.shouldClose = null; _this.moveFromContentToOverlay = null; return _this; } _createClass(ModalPortal2, [{ key: "componentDidMount", value: function componentDidMount() { if (this.props.isOpen) { this.open(); } } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps, prevState) { if (this.props.isOpen && !prevProps.isOpen) { this.open(); } else if (!this.props.isOpen && prevProps.isOpen) { this.close(); } if (this.props.shouldFocusAfterRender && this.state.isOpen && !prevState.isOpen) { this.focusContent(); } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { if (this.state.isOpen) { this.afterClose(); } clearTimeout(this.closeTimer); cancelAnimationFrame(this.openAnimationFrame); } }, { key: "beforeOpen", value: function beforeOpen() { var _props = this.props, appElement = _props.appElement, ariaHideApp = _props.ariaHideApp, htmlOpenClassName = _props.htmlOpenClassName, bodyOpenClassName = _props.bodyOpenClassName, parentSelector = _props.parentSelector; var parentDocument = parentSelector && parentSelector().ownerDocument || document; bodyOpenClassName && classList.add(parentDocument.body, bodyOpenClassName); htmlOpenClassName && classList.add(parentDocument.getElementsByTagName("html")[0], htmlOpenClassName); if (ariaHideApp) { ariaHiddenInstances += 1; ariaAppHider.hide(appElement); } _portalOpenInstances2.default.register(this); } // Don't steal focus from inner elements }, { key: "render", value: function render() { var _props2 = this.props, id = _props2.id, className = _props2.className, overlayClassName = _props2.overlayClassName, defaultStyles = _props2.defaultStyles, children = _props2.children; var contentStyles = className ? {} : defaultStyles.content; var overlayStyles = overlayClassName ? {} : defaultStyles.overlay; if (this.shouldBeClosed()) { return null; } var overlayProps = { ref: this.setOverlayRef, className: this.buildClassName("overlay", overlayClassName), style: _extends({}, overlayStyles, this.props.style.overlay), onClick: this.handleOverlayOnClick, onMouseDown: this.handleOverlayOnMouseDown }; var contentProps = _extends({ id, ref: this.setContentRef, style: _extends({}, contentStyles, this.props.style.content), className: this.buildClassName("content", className), tabIndex: "-1", onKeyDown: this.handleKeyDown, onMouseDown: this.handleContentOnMouseDown, onMouseUp: this.handleContentOnMouseUp, onClick: this.handleContentOnClick, role: this.props.role, "aria-label": this.props.contentLabel }, this.attributesFromObject("aria", _extends({ modal: true }, this.props.aria)), this.attributesFromObject("data", this.props.data || {}), { "data-testid": this.props.testId }); var contentElement = this.props.contentElement(contentProps, children); return this.props.overlayElement(overlayProps, contentElement); } }]); return ModalPortal2; }(_react.Component); ModalPortal.defaultProps = { style: { overlay: {}, content: {} }, defaultStyles: {} }; ModalPortal.propTypes = { isOpen: _propTypes2.default.bool.isRequired, defaultStyles: _propTypes2.default.shape({ content: _propTypes2.default.object, overlay: _propTypes2.default.object }), style: _propTypes2.default.shape({ content: _propTypes2.default.object, overlay: _propTypes2.default.object }), className: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.object]), overlayClassName: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.object]), parentSelector: _propTypes2.default.func, bodyOpenClassName: _propTypes2.default.string, htmlOpenClassName: _propTypes2.default.string, ariaHideApp: _propTypes2.default.bool, appElement: _propTypes2.default.oneOfType([_propTypes2.default.instanceOf(_safeHTMLElement2.default), _propTypes2.default.instanceOf(_safeHTMLElement.SafeHTMLCollection), _propTypes2.default.instanceOf(_safeHTMLElement.SafeNodeList), _propTypes2.default.arrayOf(_propTypes2.default.instanceOf(_safeHTMLElement2.default))]), onAfterOpen: _propTypes2.default.func, onAfterClose: _propTypes2.default.func, onRequestClose: _propTypes2.default.func, closeTimeoutMS: _propTypes2.default.number, shouldFocusAfterRender: _propTypes2.default.bool, shouldCloseOnOverlayClick: _propTypes2.default.bool, shouldReturnFocusAfterClose: _propTypes2.default.bool, preventScroll: _propTypes2.default.bool, role: _propTypes2.default.string, contentLabel: _propTypes2.default.string, aria: _propTypes2.default.object, data: _propTypes2.default.object, children: _propTypes2.default.node, shouldCloseOnEsc: _propTypes2.default.bool, overlayRef: _propTypes2.default.func, contentRef: _propTypes2.default.func, id: _propTypes2.default.string, overlayElement: _propTypes2.default.func, contentElement: _propTypes2.default.func, testId: _propTypes2.default.string }; exports.default = ModalPortal; module.exports = exports["default"]; } (ModalPortal, ModalPortal.exports)); var ModalPortalExports = ModalPortal.exports; /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ function componentWillMount() { // Call this.constructor.gDSFP to support sub-classes. var state = this.constructor.getDerivedStateFromProps(this.props, this.state); if (state !== null && state !== undefined) { this.setState(state); } } function componentWillReceiveProps(nextProps) { // Call this.constructor.gDSFP to support sub-classes. // Use the setState() updater to ensure state isn't stale in certain edge cases. function updater(prevState) { var state = this.constructor.getDerivedStateFromProps(nextProps, prevState); return state !== null && state !== undefined ? state : null; } // Binding "this" is important for shallow renderer support. this.setState(updater.bind(this)); } function componentWillUpdate(nextProps, nextState) { try { var prevProps = this.props; var prevState = this.state; this.props = nextProps; this.state = nextState; this.__reactInternalSnapshotFlag = true; this.__reactInternalSnapshot = this.getSnapshotBeforeUpdate( prevProps, prevState ); } finally { this.props = prevProps; this.state = prevState; } } // React may warn about cWM/cWRP/cWU methods being deprecated. // Add a flag to suppress these warnings for this special case. componentWillMount.__suppressDeprecationWarning = true; componentWillReceiveProps.__suppressDeprecationWarning = true; componentWillUpdate.__suppressDeprecationWarning = true; function polyfill(Component) { var prototype = Component.prototype; if (!prototype || !prototype.isReactComponent) { throw new Error('Can only polyfill class components'); } if ( typeof Component.getDerivedStateFromProps !== 'function' && typeof prototype.getSnapshotBeforeUpdate !== 'function' ) { return Component; } // If new component APIs are defined, "unsafe" lifecycles won't be called. // Error if any of these lifecycles are present, // Because they would work differently between older and newer (16.3+) versions of React. var foundWillMountName = null; var foundWillReceivePropsName = null; var foundWillUpdateName = null; if (typeof prototype.componentWillMount === 'function') { foundWillMountName = 'componentWillMount'; } else if (typeof prototype.UNSAFE_componentWillMount === 'function') { foundWillMountName = 'UNSAFE_componentWillMount'; } if (typeof prototype.componentWillReceiveProps === 'function') { foundWillReceivePropsName = 'componentWillReceiveProps'; } else if (typeof prototype.UNSAFE_componentWillReceiveProps === 'function') { foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps'; } if (typeof prototype.componentWillUpdate === 'function') { foundWillUpdateName = 'componentWillUpdate'; } else if (typeof prototype.UNSAFE_componentWillUpdate === 'function') { foundWillUpdateName = 'UNSAFE_componentWillUpdate'; } if ( foundWillMountName !== null || foundWillReceivePropsName !== null || foundWillUpdateName !== null ) { var componentName = Component.displayName || Component.name; var newApiName = typeof Component.getDerivedStateFromProps === 'function' ? 'getDerivedStateFromProps()' : 'getSnapshotBeforeUpdate()'; throw Error( 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + componentName + ' uses ' + newApiName + ' but also contains the following legacy lifecycles:' + (foundWillMountName !== null ? '\n ' + foundWillMountName : '') + (foundWillReceivePropsName !== null ? '\n ' + foundWillReceivePropsName : '') + (foundWillUpdateName !== null ? '\n ' + foundWillUpdateName : '') + '\n\nThe above lifecycles should be removed. Learn more about this warning here:\n' + 'https://fb.me/react-async-component-lifecycle-hooks' ); } // React <= 16.2 does not support static getDerivedStateFromProps. // As a workaround, use cWM and cWRP to invoke the new static lifecycle. // Newer versions of React will ignore these lifecycles if gDSFP exists. if (typeof Component.getDerivedStateFromProps === 'function') { prototype.componentWillMount = componentWillMount; prototype.componentWillReceiveProps = componentWillReceiveProps; } // React <= 16.2 does not support getSnapshotBeforeUpdate. // As a workaround, use cWU to invoke the new lifecycle. // Newer versions of React will ignore that lifecycle if gSBU exists. if (typeof prototype.getSnapshotBeforeUpdate === 'function') { if (typeof prototype.componentDidUpdate !== 'function') { throw new Error( 'Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype' ); } prototype.componentWillUpdate = componentWillUpdate; var componentDidUpdate = prototype.componentDidUpdate; prototype.componentDidUpdate = function componentDidUpdatePolyfill( prevProps, prevState, maybeSnapshot ) { // 16.3+ will not execute our will-update method; // It will pass a snapshot value to did-update though. // Older versions will require our polyfilled will-update value. // We need to handle both cases, but can't just check for the presence of "maybeSnapshot", // Because for <= 15.x versions this might be a "prevContext" object. // We also can't just check "__reactInternalSnapshot", // Because get-snapshot might return a falsy value. // So check for the explicit __reactInternalSnapshotFlag flag to determine behavior. var snapshot = this.__reactInternalSnapshotFlag ? this.__reactInternalSnapshot : maybeSnapshot; componentDidUpdate.call(this, prevProps, prevState, snapshot); }; } return Component; } const reactLifecyclesCompat_es = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ __proto__: null, polyfill }, Symbol.toStringTag, { value: 'Module' })); const require$$6 = /*@__PURE__*/getAugmentedNamespace(reactLifecyclesCompat_es); Object.defineProperty(Modal$2, "__esModule", { value: true }); Modal$2.bodyOpenClassName = Modal$2.portalClassName = void 0; var _extends$1 = Object.assign || function(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = /* @__PURE__ */ function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = reactExports; var _react2 = _interopRequireDefault$e(_react); var _reactDom = reactDomExports; var _reactDom2 = _interopRequireDefault$e(_reactDom); var _propTypes = propTypesExports; var _propTypes2 = _interopRequireDefault$e(_propTypes); var _ModalPortal = ModalPortalExports; var _ModalPortal2 = _interopRequireDefault$e(_ModalPortal); var _ariaAppHider = ariaAppHider$1; var ariaAppHider = _interopRequireWildcard$1(_ariaAppHider); var _safeHTMLElement = safeHTMLElement; var _safeHTMLElement2 = _interopRequireDefault$e(_safeHTMLElement); var _reactLifecyclesCompat = require$$6; function _interopRequireWildcard$1(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault$e(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var portalClassName = Modal$2.portalClassName = "ReactModalPortal"; var bodyOpenClassName = Modal$2.bodyOpenClassName = "ReactModal__Body--open"; var isReact16 = _safeHTMLElement.canUseDOM && _reactDom2.default.createPortal !== void 0; var createHTMLElement = function createHTMLElement2(name) { return document.createElement(name); }; var getCreatePortal = function getCreatePortal2() { return isReact16 ? _reactDom2.default.createPortal : _reactDom2.default.unstable_renderSubtreeIntoContainer; }; function getParentElement(parentSelector2) { return parentSelector2(); } var Modal$1 = function(_Component) { _inherits(Modal2, _Component); function Modal2() { var _ref; var _temp, _this, _ret; _classCallCheck(this, Modal2); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Modal2.__proto__ || Object.getPrototypeOf(Modal2)).call.apply(_ref, [this].concat(args))), _this), _this.removePortal = function() { !isReact16 && _reactDom2.default.unmountComponentAtNode(_this.node); var parent = getParentElement(_this.props.parentSelector); if (parent && parent.contains(_this.node)) { parent.removeChild(_this.node); } else { console.warn('React-Modal: "parentSelector" prop did not returned any DOM element. Make sure that the parent element is unmounted to avoid any memory leaks.'); } }, _this.portalRef = function(ref) { _this.portal = ref; }, _this.renderPortal = function(props) { var createPortal = getCreatePortal(); var portal = createPortal(_this, _react2.default.createElement(_ModalPortal2.default, _extends$1({ defaultStyles: Modal2.defaultStyles }, props)), _this.node); _this.portalRef(portal); }, _temp), _possibleConstructorReturn(_this, _ret); } _createClass(Modal2, [{ key: "componentDidMount", value: function componentDidMount() { if (!_safeHTMLElement.canUseDOM) return; if (!isReact16) { this.node = createHTMLElement("div"); } this.node.className = this.props.portalClassName; var parent = getParentElement(this.props.parentSelector); parent.appendChild(this.node); !isReact16 && this.renderPortal(this.props); } }, { key: "getSnapshotBeforeUpdate", value: function getSnapshotBeforeUpdate(prevProps) { var prevParent = getParentElement(prevProps.parentSelector); var nextParent = getParentElement(this.props.parentSelector); return { prevParent, nextParent }; } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps, _, snapshot) { if (!_safeHTMLElement.canUseDOM) return; var _props = this.props, isOpen = _props.isOpen, portalClassName2 = _props.portalClassName; if (prevProps.portalClassName !== portalClassName2) { this.node.className = portalClassName2; } var prevParent = snapshot.prevParent, nextParent = snapshot.nextParent; if (nextParent !== prevParent) { prevParent.removeChild(this.node); nextParent.appendChild(this.node); } if (!prevProps.isOpen && !isOpen) return; !isReact16 && this.renderPortal(this.props); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { if (!_safeHTMLElement.canUseDOM || !this.node || !this.portal) return; var state = this.portal.state; var now = Date.now(); var closesAt = state.isOpen && this.props.closeTimeoutMS && (state.closesAt || now + this.props.closeTimeoutMS); if (closesAt) { if (!state.beforeClose) { this.portal.closeWithTimeout(); } setTimeout(this.removePortal, closesAt - now); } else { this.removePortal(); } } }, { key: "render", value: function render() { if (!_safeHTMLElement.canUseDOM || !isReact16) { return null; } if (!this.node && isReact16) { this.node = createHTMLElement("div"); } var createPortal = getCreatePortal(); return createPortal(_react2.default.createElement(_ModalPortal2.default, _extends$1({ ref: this.portalRef, defaultStyles: Modal2.defaultStyles }, this.props)), this.node); } }], [{ key: "setAppElement", value: function setAppElement(element) { ariaAppHider.setElement(element); } /* eslint-disable react/no-unused-prop-types */ /* eslint-enable react/no-unused-prop-types */ }]); return Modal2; }(_react.Component); Modal$1.propTypes = { isOpen: _propTypes2.default.bool.isRequired, style: _propTypes2.default.shape({ content: _propTypes2.default.object, overlay: _propTypes2.default.object }), portalClassName: _propTypes2.default.string, bodyOpenClassName: _propTypes2.default.string, htmlOpenClassName: _propTypes2.default.string, className: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.shape({ base: _propTypes2.default.string.isRequired, afterOpen: _propTypes2.default.string.isRequired, beforeClose: _propTypes2.default.string.isRequired })]), overlayClassName: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.shape({ base: _propTypes2.default.string.isRequired, afterOpen: _propTypes2.default.string.isRequired, beforeClose: _propTypes2.default.string.isRequired })]), appElement: _propTypes2.default.oneOfType([_propTypes2.default.instanceOf(_safeHTMLElement2.default), _propTypes2.default.instanceOf(_safeHTMLElement.SafeHTMLCollection), _propTypes2.default.instanceOf(_safeHTMLElement.SafeNodeList), _propTypes2.default.arrayOf(_propTypes2.default.instanceOf(_safeHTMLElement2.default))]), onAfterOpen: _propTypes2.default.func, onRequestClose: _propTypes2.default.func, closeTimeoutMS: _propTypes2.default.number, ariaHideApp: _propTypes2.default.bool, shouldFocusAfterRender: _propTypes2.default.bool, shouldCloseOnOverlayClick: _propTypes2.default.bool, shouldReturnFocusAfterClose: _propTypes2.default.bool, preventScroll: _propTypes2.default.bool, parentSelector: _propTypes2.default.func, aria: _propTypes2.default.object, data: _propTypes2.default.object, role: _propTypes2.default.string, contentLabel: _propTypes2.default.string, shouldCloseOnEsc: _propTypes2.default.bool, overlayRef: _propTypes2.default.func, contentRef: _propTypes2.default.func, id: _propTypes2.default.string, overlayElement: _propTypes2.default.func, contentElement: _propTypes2.default.func }; Modal$1.defaultProps = { isOpen: false, portalClassName, bodyOpenClassName, role: "dialog", ariaHideApp: true, closeTimeoutMS: 0, shouldFocusAfterRender: true, shouldCloseOnEsc: true, shouldCloseOnOverlayClick: true, shouldReturnFocusAfterClose: true, preventScroll: false, parentSelector: function parentSelector() { return document.body; }, overlayElement: function overlayElement(props, contentEl) { return _react2.default.createElement( "div", props, contentEl ); }, contentElement: function contentElement(props, children) { return _react2.default.createElement( "div", props, children ); } }; Modal$1.defaultStyles = { overlay: { position: "fixed", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "rgba(255, 255, 255, 0.75)" }, content: { position: "absolute", top: "40px", left: "40px", right: "40px", bottom: "40px", border: "1px solid #ccc", background: "#fff", overflow: "auto", WebkitOverflowScrolling: "touch", borderRadius: "4px", outline: "none", padding: "20px" } }; (0, _reactLifecyclesCompat.polyfill)(Modal$1); Modal$2.default = Modal$1; (function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); var _Modal = Modal$2; var _Modal2 = _interopRequireDefault(_Modal); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = _Modal2.default; module.exports = exports["default"]; } (lib, lib.exports)); var libExports = lib.exports; const Modal = /*@__PURE__*/getDefaultExportFromCjs(libExports); let isInitialized = false; const initializeApp = () => { if (isInitialized) { return; } logger.debug("app", "πŸš€ App initializing", { isDevMode: true === "true", environment: "production" }); Modal.setAppElement("#root"); isInitialized = true; }; const themeOptions = { palette: { primary: { main: "#1976d2", light: "#42a5f5", dark: "#1565c0", contrastText: "#ffffff" }, secondary: { main: "#dc004e", light: "#ff4081", dark: "#c51162", contrastText: "#ffffff" }, error: { main: "#f44336", light: "#e57373", dark: "#d32f2f" }, warning: { main: "#ff9800", light: "#ffb74d", dark: "#f57c00" }, info: { main: "#2196f3", light: "#64b5f6", dark: "#1976d2" }, success: { main: "#4caf50", light: "#81c784", dark: "#388e3c" }, background: { default: "#f5f5f5", paper: "#ffffff" }, text: { primary: "rgba(0, 0, 0, 0.87)", secondary: "rgba(0, 0, 0, 0.6)", disabled: "rgba(0, 0, 0, 0.38)" } }, typography: { fontFamily: [ "-apple-system", "BlinkMacSystemFont", '"Segoe UI"', "Roboto", '"Helvetica Neue"', "Arial", "sans-serif" ].join(","), h1: { fontSize: "2.5rem", fontWeight: 500 }, h2: { fontSize: "2rem", fontWeight: 500 }, h3: { fontSize: "1.75rem", fontWeight: 500 }, h4: { fontSize: "1.5rem", fontWeight: 500 }, h5: { fontSize: "1.25rem", fontWeight: 500 }, h6: { fontSize: "1rem", fontWeight: 500 }, body1: { fontSize: "1rem", lineHeight: 1.5 }, body2: { fontSize: "0.875rem", lineHeight: 1.43 } }, shape: { borderRadius: 4 }, components: { MuiButton: { styleOverrides: { root: { textTransform: "none", borderRadius: "4px", padding: "6px 16px" }, contained: { boxShadow: "none", "&:hover": { boxShadow: "0px 2px 4px -1px rgba(0,0,0,0.2)" } } } }, MuiTextField: { styleOverrides: { root: { "& .MuiOutlinedInput-root": { borderRadius: "4px" } } } }, MuiCard: { styleOverrides: { root: { borderRadius: "8px", boxShadow: "0px 2px 4px -1px rgba(0,0,0,0.1)" } } }, MuiAppBar: { styleOverrides: { root: { boxShadow: "0px 1px 3px rgba(0,0,0,0.12)" } } } }, breakpoints: { values: { xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920 } } }; const theme = createTheme(themeOptions); const supabaseUrl = "http://localhost:8000"; const supabaseAnonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0"; logger.info("supabase-client", "πŸ”„ Supabase configuration", { url: supabaseUrl, key: supabaseAnonKey }); let supabaseInstance = null; const getSupabaseClient = () => { if (!supabaseInstance) { logger.info("supabase-client", "πŸ”„ Initializing Supabase client"); supabaseInstance = createClient( supabaseUrl, supabaseAnonKey, { auth: { flowType: "pkce", autoRefreshToken: true, persistSession: true, detectSessionInUrl: false, storage: window.localStorage, storageKey: "supabase.auth.token", debug: true }, global: { headers: { "X-Client-Info": "classroom-copilot" } } } ); logger.info("supabase-client", "πŸ”„ Supabase client configuration loaded", { url: supabaseUrl, hasKey: true, storageKey: "supabase.auth.token" }); } return supabaseInstance; }; const supabase = new Proxy({}, { get: (target, prop) => { const client = getSupabaseClient(); return client[prop]; } }); var StorageKeys = /* @__PURE__ */ ((StorageKeys2) => { StorageKeys2["USER"] = "user"; StorageKeys2["USER_ROLE"] = "user_role"; StorageKeys2["SUPABASE_TOKEN"] = "supabase_token"; StorageKeys2["MS_TOKEN"] = "msAccessToken"; StorageKeys2["NEO4J_USER_DB"] = "neo4jUserDbName"; StorageKeys2["NEO4J_WORKER_DB"] = "neo4jWorkerDbName"; StorageKeys2["USER_NODES"] = "userNodes"; StorageKeys2["CALENDAR_DATA"] = "calendarData"; StorageKeys2["IS_NEW_REGISTRATION"] = "isNewRegistration"; StorageKeys2["TLDRAW_PREFERENCES"] = "tldrawUserPreferences"; StorageKeys2["TLDRAW_FILE_PATH"] = "tldrawUserFilePath"; StorageKeys2["LOCAL_SNAPSHOT"] = "localSnapshot"; StorageKeys2["NODE_FILE_PATH"] = "nodeFilePath"; StorageKeys2["ONENOTE_NOTEBOOK"] = "oneNoteNotebook"; StorageKeys2["PRESENTATION_MODE"] = "presentationMode"; StorageKeys2["TLDRAW_USER"] = "tldrawUser"; return StorageKeys2; })(StorageKeys || {}); class StorageService { static instance; constructor() { } static getInstance() { if (!StorageService.instance) { StorageService.instance = new StorageService(); } return StorageService.instance; } get(key) { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : null; } catch (error) { logger.error("storage-service", `Error retrieving ${key}:`, error); return null; } } set(key, value) { try { const serializedValue = JSON.stringify(value); localStorage.setItem(key, serializedValue); logger.debug("storage-service", `Stored ${key} in localStorage`); } catch (error) { logger.error("storage-service", `Error storing ${key}:`, error); } } remove(key) { try { localStorage.removeItem(key); logger.debug("storage-service", `Removed ${key} from localStorage`); } catch (error) { logger.error("storage-service", `Error removing ${key}:`, error); } } clearAll() { try { Object.values(StorageKeys).forEach((key) => { localStorage.removeItem(key); }); logger.debug("storage-service", "Cleared all app items from localStorage"); } catch (error) { logger.error("storage-service", "Error clearing storage:", error); } } // Helper method to update state and storage together setStateAndStorage(setter, key, value) { setter(value); this.set(key, value); } } const storageService = StorageService.getInstance(); class DatabaseNameService { static CC_USERS = "cc.users"; static CC_SCHOOLS = "cc.institutes"; static getUserPrivateDB(userType, username) { const dbName = `${this.CC_USERS}.${userType}.${username}`; logger.debug("database-name-service", "πŸ“₯ Generating user private DB name", { userType, username, dbName }); return dbName; } static getSchoolPrivateDB(schoolId) { const dbName = `${this.CC_SCHOOLS}.${schoolId}`; logger.debug("database-name-service", "πŸ“₯ Generating school private DB name", { schoolId, dbName }); return dbName; } static getDevelopmentSchoolDB() { const dbName = `${this.CC_SCHOOLS}.development.default`; logger.debug("database-name-service", "πŸ“₯ Getting default school DB name", { dbName }); return dbName; } static getContextDatabase(context, userType, username) { logger.debug("database-name-service", "πŸ“₯ Resolving context database", { context, userType, username }); if (["school", "department", "class"].includes(context)) { logger.debug("database-name-service", "βœ… Using schools database for context", { context, dbName: this.CC_SCHOOLS }); return this.CC_SCHOOLS; } const userDb = this.getUserPrivateDB(userType, username); logger.debug("database-name-service", "βœ… Using user private database for context", { context, dbName: userDb }); return userDb; } } function convertToCCUser(user, metadata) { const username = metadata.username || metadata.preferred_username || metadata.email?.split("@")[0] || user.email?.split("@")[0] || "user"; const displayName = metadata.display_name || metadata.name || metadata.preferred_username || username; const userType = metadata.user_type || "student"; const userDbName = DatabaseNameService.getUserPrivateDB( userType, username ); const schoolDbName = DatabaseNameService.getDevelopmentSchoolDB(); return { id: user.id, email: user.email, user_type: userType, username, display_name: displayName, user_db_name: userDbName, school_db_name: schoolDbName, created_at: user.created_at, updated_at: user.updated_at }; } class AuthService { static instance; constructor() { } onAuthStateChange(callback) { return supabase.auth.onAuthStateChange((event, session) => { logger.info("auth-service", "πŸ”„ Auth state changed", { event, hasSession: !!session, userId: session?.user?.id, eventType: event }); if (event === "SIGNED_OUT") { storageService.clearAll(); } callback(event, session); }); } static getInstance() { if (!AuthService.instance) { AuthService.instance = new AuthService(); } return AuthService.instance; } async getCurrentSession() { try { const { data: { session }, error } = await supabase.auth.getSession(); if (error) { throw error; } if (!session) { return { user: null, accessToken: null, message: "No active session" }; } return { user: convertToCCUser( session.user, session.user.user_metadata ), accessToken: session.access_token, message: "Session retrieved" }; } catch (error) { logger.error("auth-service", "Failed to get current session:", error); throw error; } } async getCurrentUser() { try { const { data: { user }, error } = await supabase.auth.getUser(); if (error || !user) { return null; } return convertToCCUser(user, user.user_metadata); } catch (error) { logger.error("auth-service", "Failed to get current user:", error); return null; } } async login({ email, password, role }) { try { logger.info("auth-service", "πŸ”„ Attempting login", { email, role }); const { data, error } = await supabase.auth.signInWithPassword({ email, password }); if (error) { logger.error("auth-service", "❌ Supabase auth error", { error: error.message, status: error.status }); throw error; } if (!data.session) { logger.error("auth-service", "❌ No session after login"); throw new Error("No session after login"); } const ccUser = convertToCCUser( data.user, data.user.user_metadata ); storageService.set(StorageKeys.USER_ROLE, ccUser.user_type); storageService.set(StorageKeys.USER, ccUser); storageService.set(StorageKeys.SUPABASE_TOKEN, data.session.access_token); logger.info("auth-service", "βœ… Login successful", { userId: ccUser.id, role: ccUser.user_type, username: ccUser.username }); return { user: ccUser, accessToken: data.session.access_token, userRole: ccUser.user_type, message: "Login successful" }; } catch (error) { logger.error("auth-service", "❌ Login failed:", error); throw error; } } async logout() { try { logger.debug("auth-service", "πŸ”„ Attempting logout"); const { error } = await supabase.auth.signOut({ scope: "local" }); if (error) { logger.error("auth-service", "❌ Logout failed:", error); throw error; } storageService.clearAll(); await supabase.auth.refreshSession(); logger.debug("auth-service", "βœ… Logout successful"); } catch (error) { logger.error("auth-service", "❌ Logout failed:", error); throw error; } } } const authService = AuthService.getInstance(); const AuthContext = reactExports.createContext({ user: null, user_role: null, loading: true, error: null, signIn: async () => { }, signOut: async () => { }, clearError: () => { } }); function AuthProvider({ children }) { const navigate = useNavigate(); const [user, setUser] = reactExports.useState(null); const [user_role, setUserRole] = reactExports.useState(null); const [loading, setLoading] = reactExports.useState(true); const [error, setError] = reactExports.useState(null); reactExports.useEffect(() => { const loadUser = async () => { try { const { data: { user: user2 } } = await supabase.auth.getUser(); if (user2) { const metadata = user2.user_metadata; setUser({ id: user2.id, email: user2.email, user_type: metadata.user_type || "", username: metadata.username || "", display_name: metadata.display_name || "", user_db_name: `cc.users.${metadata.user_type}.${metadata.username}`, school_db_name: "cc.institutes.development.default", created_at: user2.created_at, updated_at: user2.updated_at }); setUserRole(metadata.user_role || null); } else { setUser(null); } } catch (error2) { logger.error("auth-context", "❌ Failed to load user", { error: error2 }); setError(error2 instanceof Error ? error2 : new Error("Failed to load user")); } finally { setLoading(false); } }; loadUser(); const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, session) => { if (event === "SIGNED_IN" && session?.user) { const metadata = session.user.user_metadata; setUser({ id: session.user.id, email: session.user.email, user_type: metadata.user_type || "", username: metadata.username || "", display_name: metadata.display_name || "", user_db_name: `cc.users.${metadata.user_type}.${metadata.username}`, school_db_name: "cc.institutes.development.default", created_at: session.user.created_at, updated_at: session.user.updated_at }); } else if (event === "SIGNED_OUT") { setUser(null); } }); return () => { subscription.unsubscribe(); }; }, []); const signIn = async (email, password) => { try { setLoading(true); const { data, error: signInError } = await supabase.auth.signInWithPassword({ email, password }); if (signInError) throw signInError; if (data.user) { const metadata = data.user.user_metadata; setUser({ id: data.user.id, email: data.user.email, user_type: metadata.user_type || "", username: metadata.username || "", display_name: metadata.display_name || "", user_db_name: `cc.users.${metadata.user_type}.${metadata.username}`, school_db_name: "cc.institutes.development.default", created_at: data.user.created_at, updated_at: data.user.updated_at }); } } catch (error2) { logger.error("auth-context", "❌ Sign in failed", { error: error2 }); setError(error2 instanceof Error ? error2 : new Error("Sign in failed")); throw error2; } finally { setLoading(false); } }; const signOut = async () => { try { setLoading(true); await authService.logout(); setUser(null); navigate("/"); } catch (error2) { logger.error("auth-context", "❌ Sign out failed", { error: error2 }); setError(error2 instanceof Error ? error2 : new Error("Sign out failed")); throw error2; } finally { setLoading(false); } }; const clearError = () => setError(null); return /* @__PURE__ */ jsxRuntimeExports.jsx( AuthContext.Provider, { value: { user, user_role, loading, error, signIn, signOut, clearError }, children } ); } const useAuth = () => reactExports.useContext(AuthContext); class PresentationService { editor; initialSlideshow = null; cameraProxyId = createShapeId("camera-proxy"); lastUserInteractionTime = 0; USER_INTERACTION_DEBOUNCE = 1e3; // 1 second zoomLevels = /* @__PURE__ */ new Map(); // Track zoom levels by shape dimensions isMoving = false; constructor(editor) { this.editor = editor; logger.debug("system", "πŸŽ₯ PresentationService initialized"); const style = document.createElement("style"); style.setAttribute("data-camera-proxy", this.cameraProxyId); style.textContent = ` [data-shape-id="${this.cameraProxyId}"] { opacity: 0 !important; pointer-events: none !important; } `; document.head.appendChild(style); } getShapeDimensionKey(width, height) { return `${Math.round(width)}_${Math.round(height)}`; } async moveToShape(shape) { if (this.isMoving) { logger.debug("presentation", "⏳ Movement in progress, queueing next movement"); await new Promise((resolve) => setTimeout(resolve, 100)); return this.moveToShape(shape); } this.isMoving = true; const bounds = this.editor.getShapePageBounds(shape.id); if (!bounds) { logger.warn("presentation", "⚠️ Could not get bounds for shape"); this.isMoving = false; return; } try { this.editor.updateShape({ id: this.cameraProxyId, type: "frame", x: bounds.minX, y: bounds.minY, props: { w: bounds.width, h: bounds.height, name: "camera-proxy" } }); await new Promise((resolve) => requestAnimationFrame(resolve)); const viewport = this.editor.getViewportPageBounds(); const padding = 32; const dimensionKey = this.getShapeDimensionKey(bounds.width, bounds.height); let targetZoom = this.zoomLevels.get(dimensionKey); if (!targetZoom) { targetZoom = Math.min( (viewport.width - padding * 2) / bounds.width, (viewport.height - padding * 2) / bounds.height ); this.zoomLevels.set(dimensionKey, targetZoom); logger.debug("presentation", "πŸ“ New zoom level calculated", { dimensions: dimensionKey, zoom: targetZoom }); } this.editor.stopCameraAnimation(); this.editor.zoomToBounds(bounds, { animation: { duration: 500, easing: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2 }, targetZoom, inset: padding }); await new Promise((resolve) => setTimeout(resolve, 500)); } catch (error) { logger.error("presentation", "❌ Error during shape transition", { error }); } finally { this.isMoving = false; } } startPresentationMode() { logger.info("presentation", "πŸŽ₯ Starting presentation mode"); this.zoomLevels.clear(); const slideshows = this.editor.getSortedChildIdsForParent(this.editor.getCurrentPageId()).map((id) => this.editor.getShape(id)).filter((shape) => shape?.type === "cc-slideshow"); if (slideshows.length === 0) { logger.warn("presentation", "⚠️ No slideshows found"); return () => { }; } this.initialSlideshow = slideshows[0]; if (!this.editor.getShape(this.cameraProxyId)) { this.editor.createShape({ id: this.cameraProxyId, type: "frame", x: 0, y: 0, props: { w: 1, h: 1, name: "camera-proxy" } }); } const handleStoreChange = (event) => { if (event.source === "user") { const now = Date.now(); if (now - this.lastUserInteractionTime > this.USER_INTERACTION_DEBOUNCE) { logger.debug("presentation", "πŸ“ User interaction received"); this.lastUserInteractionTime = now; } } if (!event.changes.updated) return; const shapeUpdates = Object.entries(event.changes.updated).filter( ([, [from, to]]) => from.typeName === "shape" && to.typeName === "shape" && from.type === "cc-slideshow" && to.type === "cc-slideshow" ); if (shapeUpdates.length === 0) return; for (const [, [from, to]] of shapeUpdates) { const fromShape = from; const toShape = to; if (!this.initialSlideshow || fromShape.id !== this.initialSlideshow.id) continue; const fromShow = fromShape; const toShow = toShape; if (fromShow.props.currentSlideIndex === toShow.props.currentSlideIndex) continue; logger.info("presentation", "πŸ”„ Moving to new slide", { from: fromShow.props.currentSlideIndex, to: toShow.props.currentSlideIndex }); const bindings = this.editor.getBindingsFromShape(toShow, "cc-slide-layout").filter((b) => b.type === "cc-slide-layout").filter((b) => !b.props.placeholder).sort((a, b) => a.props.index > b.props.index ? 1 : -1); const currentBinding = bindings[toShow.props.currentSlideIndex]; if (!currentBinding) { logger.warn("presentation", "⚠️ Could not find binding for target slide"); continue; } const currentSlide = this.editor.getShape(currentBinding.toId); if (!currentSlide) { logger.warn("presentation", "⚠️ Could not find target slide"); continue; } void this.moveToShape(currentSlide); } }; const storeCleanup = this.editor.store.listen(handleStoreChange); return () => { logger.info("presentation", "🧹 Running presentation mode cleanup"); storeCleanup(); this.stopPresentationMode(); }; } stopPresentationMode() { this.zoomLevels.clear(); this.isMoving = false; if (this.editor.getShape(this.cameraProxyId)) { this.editor.deleteShape(this.cameraProxyId); } const style = document.querySelector(`style[data-camera-proxy="${this.cameraProxyId}"]`); if (style) { style.remove(); } } // Public method to move to any shape (slide or slideshow) zoomToShape(shape) { void this.moveToShape(shape); } } const TLDrawContext = reactExports.createContext({ tldrawPreferences: null, tldrawUserFilePath: null, localSnapshot: null, presentationMode: false, sharedStore: null, connectionStatus: "online", presentationService: null, setTldrawPreferences: () => { }, setTldrawUserFilePath: () => { }, handleLocalSnapshot: async () => { }, togglePresentationMode: () => { }, initializePreferences: () => { }, setSharedStore: () => { }, setConnectionStatus: () => { } }); const TLDrawProvider = ({ children }) => { const [tldrawPreferences, setTldrawPreferencesState] = reactExports.useState( storageService.get(StorageKeys.TLDRAW_PREFERENCES) ); const [tldrawUserFilePath, setTldrawUserFilePathState] = reactExports.useState( storageService.get(StorageKeys.TLDRAW_FILE_PATH) ); const [localSnapshot, setLocalSnapshot] = reactExports.useState( storageService.get(StorageKeys.LOCAL_SNAPSHOT) ); const [presentationMode, setPresentationMode] = reactExports.useState( storageService.get(StorageKeys.PRESENTATION_MODE) || false ); const [sharedStore, setSharedStore] = reactExports.useState(null); const [connectionStatus, setConnectionStatus] = reactExports.useState("online"); const [presentationService, setPresentationService] = reactExports.useState(null); const initializePreferences = reactExports.useCallback((userId) => { logger.debug("tldraw-context", "πŸ”„ Initializing TLDraw preferences"); const storedPrefs = storageService.get(StorageKeys.TLDRAW_PREFERENCES); if (storedPrefs) { logger.debug("tldraw-context", "πŸ“₯ Found stored preferences"); setTldrawPreferencesState(storedPrefs); return; } const defaultPrefs = { id: userId, name: "User", color: `hsl(${Math.random() * 360}, 70%, 50%)`, locale: "en", colorScheme: "system", isSnapMode: false, isWrapMode: false, isDynamicSizeMode: false, isPasteAtCursorMode: false, animationSpeed: 1, edgeScrollSpeed: 1 }; logger.debug("tldraw-context", "πŸ“ Creating default preferences"); storageService.set(StorageKeys.TLDRAW_PREFERENCES, defaultPrefs); setTldrawPreferencesState(defaultPrefs); }, []); const setTldrawPreferences = reactExports.useCallback((preferences) => { logger.debug("tldraw-context", "πŸ”„ Setting TLDraw preferences", { preferences }); if (preferences) { storageService.set(StorageKeys.TLDRAW_PREFERENCES, preferences); } else { storageService.remove(StorageKeys.TLDRAW_PREFERENCES); } setTldrawPreferencesState(preferences); }, []); const setTldrawUserFilePath = (path) => { logger.debug("tldraw-context", "πŸ”„ Setting TLDraw user file path"); if (path) { storageService.set(StorageKeys.TLDRAW_FILE_PATH, path); } else { storageService.remove(StorageKeys.TLDRAW_FILE_PATH); } setTldrawUserFilePathState(path); }; const handleLocalSnapshot = reactExports.useCallback(async (action, store, setLoadingState) => { if (!store) { setLoadingState({ status: "error", error: "Store not initialized" }); return; } try { if (sharedStore) { if (action === "put") { const snapshot = getSnapshot(store); await sharedStore.saveSnapshot(snapshot, setLoadingState); } else if (action === "get") { const savedSnapshot = storageService.get(StorageKeys.LOCAL_SNAPSHOT); if (savedSnapshot) { await sharedStore.loadSnapshot(savedSnapshot, setLoadingState); } } } else if (action === "put") { logger.debug("tldraw-context", "πŸ’Ύ Putting snapshot into local storage"); const snapshot = getSnapshot(store); logger.debug("tldraw-context", "πŸ“¦ Snapshot:", snapshot); setLocalSnapshot(snapshot); storageService.set(StorageKeys.LOCAL_SNAPSHOT, snapshot); setLoadingState({ status: "ready", error: "" }); } else if (action === "get") { logger.debug("tldraw-context", "πŸ“‚ Getting snapshot from local storage"); setLoadingState({ status: "loading", error: "" }); const savedSnapshot = storageService.get(StorageKeys.LOCAL_SNAPSHOT); if (savedSnapshot && savedSnapshot.document && savedSnapshot.session) { try { logger.debug("tldraw-context", "πŸ“₯ Loading snapshot into editor"); loadSnapshot(store, savedSnapshot); setLoadingState({ status: "ready", error: "" }); } catch (error) { logger.error("tldraw-context", "❌ Failed to load snapshot:", error); store.clear(); setLoadingState({ status: "error", error: "Failed to load snapshot" }); } } else { logger.debug("tldraw-context", "⚠️ No valid snapshot found in local storage"); setLoadingState({ status: "ready", error: "" }); } } } catch (error) { logger.error("tldraw-context", "❌ Error handling local snapshot:", error); setLoadingState({ status: "error", error: error instanceof Error ? error.message : "Unknown error" }); } }, [sharedStore]); const togglePresentationMode = reactExports.useCallback((editor) => { logger.debug("tldraw-context", "πŸ”„ Toggling presentation mode"); setPresentationMode((prev) => { const newValue = !prev; storageService.set(StorageKeys.PRESENTATION_MODE, newValue); if (newValue && editor) { logger.info("presentation", "πŸŽ₯ Initializing presentation service"); const service = new PresentationService(editor); setPresentationService(service); service.startPresentationMode(); } else if (!newValue && presentationService) { logger.info("presentation", "πŸ›‘ Stopping presentation service"); presentationService.stopPresentationMode(); setPresentationService(null); } return newValue; }); }, [presentationService]); return /* @__PURE__ */ jsxRuntimeExports.jsx( TLDrawContext.Provider, { value: { tldrawPreferences, tldrawUserFilePath, localSnapshot, presentationMode, sharedStore, connectionStatus, presentationService, setTldrawPreferences, setTldrawUserFilePath, handleLocalSnapshot, togglePresentationMode, initializePreferences, setSharedStore, setConnectionStatus }, children } ); }; const useTLDraw = () => reactExports.useContext(TLDrawContext); const UserContext = reactExports.createContext({ user: null, loading: true, error: null, profile: null, preferences: {}, isMobile: false, isInitialized: false, updateProfile: async () => { }, updatePreferences: async () => { }, clearError: () => { } }); function UserProvider({ children }) { const [user] = reactExports.useState(null); const [profile, setProfile] = reactExports.useState(null); const [preferences, setPreferences] = reactExports.useState({}); const [loading, setLoading] = reactExports.useState(true); const [isInitialized, setIsInitialized] = reactExports.useState(false); const [error, setError] = reactExports.useState(null); const [isMobile] = reactExports.useState(window.innerWidth <= 768); reactExports.useEffect(() => { const loadUserProfile = async () => { try { const { data: { user: user2 } } = await supabase.auth.getUser(); if (!user2) { setProfile(null); setLoading(false); setIsInitialized(true); return; } const { data, error: error2 } = await supabase.from("profiles").select("*").eq("id", user2.id).single(); if (error2) { throw error2; } const metadata = user2.user_metadata; const userDbName = DatabaseNameService.getUserPrivateDB(metadata.user_type || "", metadata.username || ""); const schoolDbName = DatabaseNameService.getDevelopmentSchoolDB(); const userProfile = { id: user2.id, email: user2.email, user_type: metadata.user_type || "", username: metadata.username || "", display_name: metadata.display_name || "", user_db_name: userDbName, school_db_name: schoolDbName, created_at: user2.created_at, updated_at: user2.updated_at }; setProfile(userProfile); logger.debug("user-context", "βœ… User profile loaded", { userId: userProfile.id, userType: userProfile.user_type, username: userProfile.username, userDbName: userProfile.user_db_name, schoolDbName: userProfile.school_db_name }); setPreferences({ theme: data.theme || "system", notifications: data.notifications_enabled || false }); } catch (error2) { logger.error("user-context", "❌ Failed to load user profile", { error: error2 }); setError(error2 instanceof Error ? error2 : new Error("Failed to load user profile")); } finally { setLoading(false); setIsInitialized(true); } }; loadUserProfile(); }, []); const updateProfile = async (updates) => { if (!user?.id || !profile) { return; } setLoading(true); try { const { error: error2 } = await supabase.from("profiles").update({ ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", user.id); if (error2) { throw error2; } setProfile((prev) => prev ? { ...prev, ...updates } : null); logger.info("user-context", "βœ… Profile updated successfully"); } catch (error2) { logger.error("user-context", "❌ Failed to update profile", { error: error2 }); setError(error2 instanceof Error ? error2 : new Error("Failed to update profile")); throw error2; } finally { setLoading(false); } }; const updatePreferences = async (updates) => { if (!user?.id) { return; } setLoading(true); try { const newPreferences = { ...preferences, ...updates }; setPreferences(newPreferences); const { error: error2 } = await supabase.from("profiles").update({ preferences: newPreferences, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", user.id); if (error2) { throw error2; } logger.info("user-context", "βœ… Preferences updated successfully"); } catch (error2) { logger.error("user-context", "❌ Failed to update preferences", { error: error2 }); setError(error2 instanceof Error ? error2 : new Error("Failed to update preferences")); throw error2; } finally { setLoading(false); } }; return /* @__PURE__ */ jsxRuntimeExports.jsx( UserContext.Provider, { value: { user: profile, loading, error, profile, preferences, isMobile, isInitialized, updateProfile, updatePreferences, clearError: () => setError(null) }, children } ); } const useUser = () => reactExports.useContext(UserContext); const baseURL = "http://localhost:8001"; const instance = axios$1.create({ baseURL, timeout: 12e4, // Increase timeout to 120 seconds for large files headers: { "Content-Type": "application/json" } }); instance.interceptors.request.use( (config) => { if (config.headers["Content-Type"] === "application/json" && config.data instanceof FormData) { delete config.headers["Content-Type"]; } logger.debug("axios", "πŸ”„ Outgoing request", { method: config.method, url: config.url, baseURL: config.baseURL }); return config; }, (error) => { logger.error("axios", "❌ Request error", error); return Promise.reject(error); } ); instance.interceptors.response.use( (response) => { logger.debug("axios", "βœ… Response received", { status: response.status, url: response.config.url }); return response; }, (error) => { if (error.response) { logger.error("axios", "❌ Response error", { status: error.response.status, url: error.config.url, data: error.response.data }); } else if (error.request) { logger.error("axios", "❌ No response received", { url: error.config.url }); } else { logger.error("axios", "❌ Request setup error", error.message); } return Promise.reject(error); } ); const { isAxiosError } = axios$1; const axios = Object.assign(instance, { isAxiosError }); function formatEmailForDatabase(email) { const sanitized = email.toLowerCase().replace("@", "at").replace(/\./g, "dot").replace(/_/g, "underscore").replace(/-/g, "dash"); return `${sanitized}`; } const DEV_SCHOOL_NAME = "default"; const DEV_SCHOOL_GROUP = "development"; const ADMIN_USER_NAME = "kcar"; const ADMIN_USER_GROUP = "admin"; class UserNeoDBService { static async fetchUserNodesData(email, userDbName, workerDbName) { try { if (!userDbName) { logger.error("neo4j-service", "❌ Attempted to fetch nodes without database name"); return null; } const formattedEmail = formatEmailForDatabase(email); const uniqueId = `User_${formattedEmail}`; logger.debug("neo4j-service", "πŸ”„ Fetching user nodes data", { email, formattedEmail, userDbName, workerDbName, uniqueId }); const userNode = await this.getDefaultNode("profile", userDbName); if (!userNode || !userNode.data) { throw new Error("Failed to fetch user node or node data missing"); } logger.debug("neo4j-service", "βœ… Found user node", { nodeId: userNode.id, type: userNode.type, hasData: !!userNode.data, userDbName, workerDbName }); const processedNodes = { privateUserNode: { ...userNode.data, __primarylabel__: "User", title: userNode.data.user_email || "User", w: 200, h: 200, headerColor: "#3e6589", backgroundColor: "#f0f0f0", isLocked: false }, connectedNodes: {} }; try { const calendarNode = await this.getDefaultNode("calendar", userDbName); if (calendarNode?.data) { processedNodes.connectedNodes.calendar = { ...calendarNode.data, __primarylabel__: "Calendar", title: calendarNode.data.calendar_name || "Calendar", w: 200, h: 200, headerColor: "#3e6589", backgroundColor: "#f0f0f0", isLocked: false }; logger.debug("neo4j-service", "βœ… Found calendar node", { nodeId: calendarNode.id, node_storage_path: calendarNode.data.node_storage_path }); } else { logger.debug("neo4j-service", "ℹ️ No calendar node found"); } } catch (error) { logger.warn("neo4j-service", "⚠️ Failed to fetch calendar node:", error); } if (workerDbName) { try { const teacherNode = await this.getDefaultNode("teaching", userDbName); if (teacherNode?.data) { processedNodes.connectedNodes.teacher = { ...teacherNode.data, __primarylabel__: "Teacher", title: teacherNode.data.teacher_name_formal || "Teacher", w: 200, h: 200, headerColor: "#3e6589", backgroundColor: "#f0f0f0", isLocked: false, user_db_name: userDbName, school_db_name: workerDbName }; logger.debug("neo4j-service", "βœ… Found teacher node", { nodeId: teacherNode.id, node_storage_path: teacherNode.data.node_storage_path, userDbName, workerDbName }); } else { logger.debug("neo4j-service", "ℹ️ No teacher node found"); } } catch (error) { logger.warn("neo4j-service", "⚠️ Failed to fetch teacher node:", error); } } logger.debug("neo4j-service", "βœ… Processed all user nodes", { hasUserNode: !!processedNodes.privateUserNode, hasCalendar: !!processedNodes.connectedNodes.calendar, hasTeacher: !!processedNodes.connectedNodes.teacher, teacherData: processedNodes.connectedNodes.teacher ? { uuid_string: processedNodes.connectedNodes.teacher.uuid_string, school_db_name: processedNodes.connectedNodes.teacher.school_db_name, node_storage_path: processedNodes.connectedNodes.teacher.node_storage_path } : null }); return processedNodes; } catch (error) { if (error instanceof Error) { logger.error("neo4j-service", "❌ Failed to fetch user nodes:", error.message); } else { logger.error("neo4j-service", "❌ Failed to fetch user nodes:", String(error)); } throw error; } } static getUserDatabaseName(userType, username) { return DatabaseNameService.getUserPrivateDB(userType, username); } static getSchoolDatabaseName(schoolId) { return DatabaseNameService.getSchoolPrivateDB(schoolId); } static getDefaultSchoolDatabaseName() { return DatabaseNameService.getDevelopmentSchoolDB(); } static async fetchNodeData(nodeId, dbName) { try { logger.debug("neo4j-service", "πŸ”„ Fetching node data", { nodeId, dbName }); const response = await axios.get("/database/tools/get-node", { params: { uuid_string: nodeId, db_name: dbName } }); if (response.data?.status === "success" && response.data.node) { return response.data.node; } return null; } catch (error) { logger.error("neo4j-service", "❌ Failed to fetch node data:", error); throw error; } } static getNodeDatabaseName(node) { if (!node || !node.node_storage_path) { logger.error("neo4j-service", "❌ Invalid node or missing node_storage_path", { node: node ? { id: node.id, type: node.type, label: node.label } : null, hasStoragePath: !!node?.node_storage_path }); throw new Error("Node is missing required storage path information"); } if (node.node_storage_path.startsWith("users/")) { const parts2 = node.node_storage_path.split("/"); if (parts2.length >= 4) { return parts2[3]; } logger.warn("neo4j-service", "⚠️ Unexpected user path format", { path: node.node_storage_path }); return `cc.users.${ADMIN_USER_GROUP}.${ADMIN_USER_NAME}`; } if (node.node_storage_path.startsWith("schools/")) { return `cc.institutes.${DEV_SCHOOL_GROUP}.${DEV_SCHOOL_NAME}`; } const parts = node.node_storage_path.split("/"); if (parts.length >= 4) { return parts[3]; } logger.warn("neo4j-service", "⚠️ Using fallback database name", { path: node.node_storage_path, nodeType: node.type }); return `cc.users.kcar`; } static async getDefaultNode(context, dbName) { try { logger.debug("neo4j-service", "πŸ”„ Fetching default node", { context, dbName }); const params = { db_name: dbName }; if (context === "overview") { const navigationStore = useNavigationStore.getState(); params.base_context = navigationStore.context.base; } const response = await axios.get( `/database/tools/get-default-node/${context}`, { params } ); if (response.data?.status === "success" && response.data.node) { return { id: response.data.node.id, node_storage_path: response.data.node.node_storage_path, type: response.data.node.type, label: response.data.node.label, data: response.data.node.data }; } return null; } catch (error) { logger.error("neo4j-service", "❌ Failed to fetch default node:", error); throw error; } } static async fetchCalendarStructure(dbName) { try { logger.debug("navigation", "πŸ”„ Fetching calendar structure", { dbName }); const response = await axios.get( `/database/calendar-structure/get-calendar-structure?db_name=${dbName}` ); if (response.data.status === "success") { logger.info("navigation", "βœ… Calendar structure fetched successfully"); return response.data.data; } throw new Error("Failed to fetch calendar structure"); } catch (error) { logger.error("navigation", "❌ Failed to fetch calendar structure:", error); throw error; } } static async fetchWorkerStructure(dbName) { try { logger.debug("navigation", "πŸ”„ Fetching worker structure", { dbName }); const response = await axios.get( `/database/worker-structure/get-worker-structure?db_name=${dbName}` ); if (response.data.status === "success") { logger.info("navigation", "βœ… Worker structure fetched successfully"); return response.data.data; } throw new Error("Failed to fetch worker structure"); } catch (error) { logger.error("navigation", "❌ Failed to fetch worker structure:", error); throw error; } } } const getShapeType = (nodeType) => { return `cc-${nodeType.replace(/([A-Z])/g, "-$1").toLowerCase().substring(1)}-node`; }; const isValidNodeType = (type) => { return type in { User: true, Developer: true, Teacher: true, Student: true, Calendar: true, TeacherTimetable: true, TimetableLesson: true, PlannedLesson: true, School: true, CalendarYear: true, CalendarMonth: true, CalendarWeek: true, CalendarDay: true, CalendarTimeChunk: true, ScienceLab: true, KeyStageSyllabus: true, YearGroupSyllabus: true, CurriculumStructure: true, Topic: true, TopicLesson: true, LearningStatement: true, SchoolTimetable: true, AcademicYear: true, AcademicTerm: true, AcademicWeek: true, AcademicDay: true, AcademicPeriod: true, RegistrationPeriod: true, PastoralStructure: true, KeyStage: true, Department: true, Room: true, SubjectClass: true, DepartmentStructure: true, UserTeacherTimetable: true, UserTimetableLesson: true }; }; const getCurrentHistoryNode = (history) => { const node = history.currentIndex === -1 || !history.nodes.length ? null : history.nodes[history.currentIndex]; logger.debug("history-management", "πŸ“ Getting current history node", { currentIndex: history.currentIndex, totalNodes: history.nodes.length, node }); return node; }; const addToHistory = (history, node) => { logger.debug("history-management", "βž• Adding node to history", { currentIndex: history.currentIndex, newNode: node, existingNodes: history.nodes.length }); const newNodes = [...history.nodes.slice(0, history.currentIndex + 1), node]; const newHistory = { nodes: newNodes, currentIndex: newNodes.length - 1 }; logger.debug("history-management", "βœ… History updated", { previousState: history, newState: newHistory }); return newHistory; }; const navigateHistory = (history, index) => { logger.debug("history-management", "πŸ”„ Navigating history", { currentIndex: history.currentIndex, targetIndex: index, totalNodes: history.nodes.length }); if (index < 0 || index >= history.nodes.length) { logger.warn("history-management", "⚠️ Invalid history navigation index", { requestedIndex: index, historyLength: history.nodes.length }); return history; } const newHistory = { nodes: history.nodes, currentIndex: index }; logger.debug("history-management", "βœ… History navigation complete", { from: history.currentIndex, to: index, node: history.nodes[index] }); return newHistory; }; const isProfileContext = (context) => { return ["profile", "calendar", "teaching"].includes(context); }; const isInstituteContext = (context) => { return ["school", "department", "class"].includes(context); }; const getContextDatabase = (context, userDbName, workerDbName) => { logger.debug("navigation-context", "πŸ”„ Getting context database", { mainContext: context.main, baseContext: context.base, userDbName, workerDbName }); if (context.main === "profile") { if (!userDbName) { logger.error("navigation-context", "❌ Missing user database name for profile context"); throw new Error("User database name is required for profile context"); } logger.debug("navigation-context", "βœ… Using user database", { dbName: userDbName }); return userDbName; } else { if (!workerDbName) { logger.error("navigation-context", "❌ Missing worker database name for institute context"); throw new Error("Worker database name is required for institute context"); } logger.debug("navigation-context", "βœ… Using worker database", { dbName: workerDbName }); return workerDbName; } }; const initialState = { main: "profile", base: "profile", node: null, history: { nodes: [], currentIndex: -1 } }; function getDefaultBaseForMain(main) { return main === "profile" ? "profile" : "school"; } function validateContextTransition(current, updates) { const newState = { ...current, ...updates }; if (updates.main) { newState.base = getDefaultBaseForMain(updates.main); } if (updates.base) { const isValid = newState.main === "profile" ? isProfileContext(updates.base) : isInstituteContext(updates.base); if (!isValid) { newState.base = getDefaultBaseForMain(newState.main); } } return newState; } const useNavigationStore = create$1((set, get) => ({ context: initialState, isLoading: false, error: null, switchContext: async (contextSwitch, userDbName, workerDbName) => { try { if (contextSwitch.main === "profile" && !userDbName) { logger.error("navigation-context", "❌ User database connection not initialized"); set({ error: "User database connection not initialized", isLoading: false }); return; } if (contextSwitch.main === "institute" && !workerDbName) { logger.error("navigation-context", "❌ Worker database connection not initialized"); set({ error: "Worker database connection not initialized", isLoading: false }); return; } logger.debug("navigation-context", "πŸ”„ Starting context switch", { from: { main: get().context.main, base: get().context.base, extended: contextSwitch.extended, nodeId: get().context.node?.id }, to: { main: contextSwitch.main, base: contextSwitch.base, extended: contextSwitch.extended }, skipBaseContextLoad: contextSwitch.skipBaseContextLoad }); set({ isLoading: true, error: null }); const currentState = get().context; const clearedState = { ...currentState, node: null }; set({ context: clearedState, isLoading: true }); let newState = { ...currentState, node: null }; if (contextSwitch.main) { newState = validateContextTransition(newState, { main: contextSwitch.main }); if (!contextSwitch.skipBaseContextLoad) { newState.base = getDefaultBaseForMain(contextSwitch.main); } logger.debug("navigation-state", "βœ… Main context updated", { previous: currentState.main, new: newState.main, defaultBase: newState.base }); } if (contextSwitch.base) { newState = validateContextTransition(newState, { base: contextSwitch.base }); logger.debug("navigation-state", "βœ… Base context updated", { previous: currentState.base, new: newState.base }); } logger.debug("navigation-state", "βœ… Context validation complete", { validatedState: newState, originalState: currentState }); const targetContext = contextSwitch.base || contextSwitch.extended || (contextSwitch.main ? getDefaultBaseForMain(contextSwitch.main) : newState.base); const dbName = getContextDatabase(newState, userDbName, workerDbName); logger.debug("context-switch", "πŸ” Fetching default node for context", { targetContext, dbName, currentState: newState }); const defaultNode = await UserNeoDBService.getDefaultNode(targetContext, dbName); if (!defaultNode) { const errorMsg = `No default node found for context: ${targetContext}`; logger.error("context-switch", "❌ Default node fetch failed", { targetContext }); set({ error: errorMsg, isLoading: false }); return; } logger.debug("context-switch", "✨ Default node fetched", { nodeId: defaultNode.id, node_storage_path: defaultNode.node_storage_path, type: defaultNode.type }); const newHistory = addToHistory(currentState.history, defaultNode); logger.debug("history-management", "πŸ“š History updated", { previousState: currentState.history, newState: newHistory, addedNode: defaultNode }); set({ context: { ...newState, node: defaultNode, history: newHistory }, isLoading: false, error: null }); logger.debug("navigation-context", "βœ… Context switch completed", { finalState: { main: newState.main, base: newState.base, nodeId: defaultNode.id } }); } catch (error) { logger.error("navigation-context", "❌ Failed to switch context:", error); set({ error: error instanceof Error ? error.message : "Failed to switch context", isLoading: false }); } }, goBack: () => { const currentState = get().context; if (currentState.history.currentIndex > 0) { const newHistory = navigateHistory(currentState.history, currentState.history.currentIndex - 1); const node = getCurrentHistoryNode(newHistory); set({ context: { ...currentState, node, history: newHistory } }); } }, goForward: () => { const currentState = get().context; if (currentState.history.currentIndex < currentState.history.nodes.length - 1) { const newHistory = navigateHistory(currentState.history, currentState.history.currentIndex + 1); const node = getCurrentHistoryNode(newHistory); set({ context: { ...currentState, node, history: newHistory } }); } }, setMainContext: async (main, userDbName, workerDbName) => { try { await get().switchContext({ main }, userDbName, workerDbName); } catch (error) { logger.error("navigation", "❌ Failed to set main context:", error); set({ error: error instanceof Error ? error.message : "Failed to set main context", isLoading: false }); } }, setBaseContext: async (base, userDbName, workerDbName) => { try { await get().switchContext({ base }, userDbName, workerDbName); } catch (error) { logger.error("navigation", "❌ Failed to set base context:", error); set({ error: error instanceof Error ? error.message : "Failed to set base context", isLoading: false }); } }, setExtendedContext: async (extended, userDbName, workerDbName) => { try { await get().switchContext({ extended }, userDbName, workerDbName); } catch (error) { logger.error("navigation", "❌ Failed to set extended context:", error); set({ error: error instanceof Error ? error.message : "Failed to set extended context", isLoading: false }); } }, navigate: async (nodeId, dbName) => { try { set({ isLoading: true, error: null }); const currentState = get().context; const existingNodeIndex = currentState.history.nodes.findIndex((n) => n.id === nodeId); if (existingNodeIndex !== -1) { logger.debug("navigation", "πŸ“ Navigating to existing node in history", { nodeId, historyIndex: existingNodeIndex, currentIndex: currentState.history.currentIndex }); const newHistory2 = navigateHistory(currentState.history, existingNodeIndex); const node2 = getCurrentHistoryNode(newHistory2); set({ context: { ...currentState, node: node2, history: newHistory2 }, isLoading: false, error: null }); return; } const nodeData = await UserNeoDBService.fetchNodeData(nodeId, dbName); if (!nodeData) { throw new Error(`Node not found: ${nodeId}`); } const node = { id: nodeId, node_storage_path: nodeData.node_data.node_storage_path || "", label: nodeData.node_data.title || nodeData.node_data.user_name || nodeId, type: nodeData.node_type }; logger.debug("navigation", "πŸ“ Adding new node to history", { nodeId: node.id, type: node.type, node_storage_path: node.node_storage_path }); const newHistory = addToHistory(currentState.history, node); set({ context: { ...currentState, node, history: newHistory }, isLoading: false, error: null }); } catch (error) { logger.error("navigation", "❌ Failed to navigate:", error); set({ error: error instanceof Error ? error.message : "Failed to navigate", isLoading: false }); } }, navigateToNode: async (node, userDbName, workerDbName) => { try { set({ isLoading: true, error: null }); if (!isValidNodeType(node.type)) { throw new Error(`Invalid node type: ${node.type}`); } const dbName = getContextDatabase(get().context, userDbName, workerDbName); await get().navigate(node.id, dbName); } catch (error) { logger.error("navigation", "❌ Failed to navigate to node:", error); set({ error: error instanceof Error ? error.message : "Failed to navigate to node", isLoading: false }); } }, refreshNavigationState: async (userDbName, workerDbName) => { try { set({ isLoading: true, error: null }); const currentState = get().context; if (currentState.node) { const dbName = getContextDatabase(currentState, userDbName, workerDbName); const nodeData = await UserNeoDBService.fetchNodeData(currentState.node.id, dbName); if (nodeData) { const node = { id: currentState.node.id, node_storage_path: nodeData.node_data.node_storage_path || "", label: nodeData.node_data.title || nodeData.node_data.user_name || currentState.node.id, type: nodeData.node_type }; set({ context: { ...currentState, node } }); } } set({ isLoading: false }); } catch (error) { logger.error("navigation", "❌ Failed to refresh navigation state:", error); set({ error: error instanceof Error ? error.message : "Failed to refresh navigation state", isLoading: false }); } } })); const NeoUserContext = reactExports.createContext({ userNode: null, calendarNode: null, workerNode: null, userDbName: null, workerDbName: null, isLoading: false, isInitialized: false, error: null, navigateToDay: async () => { }, navigateToWeek: async () => { }, navigateToMonth: async () => { }, navigateToYear: async () => { }, navigateToTimetable: async () => { }, navigateToJournal: async () => { }, navigateToPlanner: async () => { }, navigateToClass: async () => { }, navigateToLesson: async () => { }, currentCalendarNode: null, currentWorkerNode: null, calendarStructure: null, workerStructure: null }); const NeoUserProvider = ({ children }) => { const { user } = useAuth(); const { profile, isInitialized: isUserInitialized } = useUser(); const navigationStore = useNavigationStore(); const [userNode, setUserNode] = reactExports.useState(null); const [calendarNode] = reactExports.useState(null); const [workerNode] = reactExports.useState(null); const [currentCalendarNode, setCurrentCalendarNode] = reactExports.useState(null); const [currentWorkerNode, setCurrentWorkerNode] = reactExports.useState(null); const [calendarStructure] = reactExports.useState(null); const [workerStructure] = reactExports.useState(null); const [userDbName, setUserDbName] = reactExports.useState(null); const [workerDbName, setWorkerDbName] = reactExports.useState(null); const [isLoading, setIsLoading] = reactExports.useState(true); const [isInitialized, setIsInitialized] = reactExports.useState(false); const [error, setError] = reactExports.useState(null); const initializationRef = React$2.useRef({ hasStarted: false, isComplete: false }); const getBaseNodeProps = () => ({ title: "", w: 200, h: 200, headerColor: "#000000", backgroundColor: "#ffffff", isLocked: false, __primarylabel__: "UserTeacherTimetable", uuid_string: "", node_storage_path: "", created: (/* @__PURE__ */ new Date()).toISOString(), merged: (/* @__PURE__ */ new Date()).toISOString(), state: { parentId: null, isPageChild: false, hasChildren: false, bindings: [] }, defaultComponent: true }); reactExports.useEffect(() => { if (!isUserInitialized || !profile || isInitialized || initializationRef.current.hasStarted) { return; } const initializeContext = async () => { try { initializationRef.current.hasStarted = true; setIsLoading(true); setError(null); const userDb = profile.user_db_name || (user?.email ? `cc.users.${user.email.replace("@", "at").replace(/\./g, "dot")}` : null); if (!userDb) { throw new Error("No user database name available"); } logger.debug("neo-user-context", "πŸ”„ Starting context initialization"); await navigationStore.switchContext({ main: "profile", base: "profile", extended: "overview" }, userDb, profile.school_db_name); const userNavigationNode = navigationStore.context.node; if (userNavigationNode?.data) { const userNodeData = { ...getBaseNodeProps(), __primarylabel__: "User", uuid_string: userNavigationNode.id, node_storage_path: userNavigationNode.node_storage_path || "", title: String(userNavigationNode.data?.user_name || "User"), user_name: String(userNavigationNode.data?.user_name || "User"), user_email: user?.email || "", user_type: "User", user_id: userNavigationNode.id, worker_node_data: JSON.stringify(userNavigationNode.data || {}) }; setUserNode(userNodeData); } setUserDbName(userDb); setWorkerDbName(profile.school_db_name); setIsInitialized(true); setIsLoading(false); initializationRef.current.isComplete = true; logger.debug("neo-user-context", "βœ… Context initialization complete"); } catch (error2) { const errorMessage = error2 instanceof Error ? error2.message : "Failed to initialize user context"; logger.error("neo-user-context", "❌ Failed to initialize context", { error: errorMessage }); setError(errorMessage); setIsLoading(false); setIsInitialized(true); initializationRef.current.isComplete = true; } }; initializeContext(); }, [user?.email, profile, isUserInitialized, navigationStore, isInitialized]); const navigateToDay = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "calendar", extended: "day" }, userDbName, workerDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "CalendarDay", uuid_string: id || node.id, node_storage_path: node.node_storage_path || "", title: node.label, name: node.label, calendar_type: "day", calendar_name: node.label, start_date: (/* @__PURE__ */ new Date()).toISOString(), end_date: (/* @__PURE__ */ new Date()).toISOString() }; setCurrentCalendarNode({ id: id || node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "CalendarDay", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to day"); } finally { setIsLoading(false); } }; const navigateToWeek = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "calendar", extended: "week" }, userDbName, workerDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "CalendarWeek", uuid_string: id || node.id, node_storage_path: node.node_storage_path || "", title: node.label, name: node.label, calendar_type: "week", calendar_name: node.label, start_date: (/* @__PURE__ */ new Date()).toISOString(), end_date: (/* @__PURE__ */ new Date()).toISOString() }; setCurrentCalendarNode({ id: id || node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "CalendarWeek", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to week"); } finally { setIsLoading(false); } }; const navigateToMonth = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "calendar", extended: "month" }, userDbName, workerDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "CalendarMonth", uuid_string: id || node.id, node_storage_path: node.node_storage_path || "", title: node.label, name: node.label, calendar_type: "month", calendar_name: node.label, start_date: (/* @__PURE__ */ new Date()).toISOString(), end_date: (/* @__PURE__ */ new Date()).toISOString() }; setCurrentCalendarNode({ id: id || node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "CalendarMonth", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to month"); } finally { setIsLoading(false); } }; const navigateToYear = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "calendar", extended: "year" }, userDbName, workerDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "CalendarYear", uuid_string: id || node.id, node_storage_path: node.node_storage_path || "", title: node.label, name: node.label, calendar_type: "year", calendar_name: node.label, start_date: (/* @__PURE__ */ new Date()).toISOString(), end_date: (/* @__PURE__ */ new Date()).toISOString() }; setCurrentCalendarNode({ id: id || node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "CalendarYear", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to year"); } finally { setIsLoading(false); } }; const navigateToTimetable = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "teaching", extended: "timetable" }, userDbName, workerDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "UserTeacherTimetable", uuid_string: id || node.id, node_storage_path: node.node_storage_path || "", title: node.label, school_db_name: workerDbName || "", school_timetable_id: id || node.id }; setCurrentWorkerNode({ id: id || node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "UserTeacherTimetable", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to timetable"); } finally { setIsLoading(false); } }; const navigateToJournal = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "teaching", extended: "journal" }, userDbName, workerDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "UserTeacherTimetable", uuid_string: id || node.id, node_storage_path: node.node_storage_path || "", title: node.label, school_db_name: workerDbName || "", school_timetable_id: id || node.id }; setCurrentWorkerNode({ id: id || node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "UserTeacherTimetable", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to journal"); } finally { setIsLoading(false); } }; const navigateToPlanner = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "teaching", extended: "planner" }, userDbName, workerDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "UserTeacherTimetable", uuid_string: id || node.id, node_storage_path: node.node_storage_path || "", title: node.label, school_db_name: workerDbName || "", school_timetable_id: id || node.id }; setCurrentWorkerNode({ id: id || node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "UserTeacherTimetable", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to planner"); } finally { setIsLoading(false); } }; const navigateToClass = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "teaching", extended: "classes" }, userDbName, workerDbName); await navigationStore.navigate(id, userDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "UserTeacherTimetable", uuid_string: node.id, node_storage_path: node.node_storage_path || "", title: node.label, school_db_name: workerDbName || "", school_timetable_id: node.id }; setCurrentWorkerNode({ id: node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "UserTeacherTimetable", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to class"); } finally { setIsLoading(false); } }; const navigateToLesson = async (id) => { if (!userDbName) return; setIsLoading(true); try { await navigationStore.switchContext({ base: "teaching", extended: "lessons" }, userDbName, workerDbName); await navigationStore.navigate(id, userDbName); const node = navigationStore.context.node; if (node?.data) { const nodeData = { ...getBaseNodeProps(), __primarylabel__: "UserTeacherTimetable", uuid_string: node.id, node_storage_path: node.node_storage_path || "", title: node.label, school_db_name: workerDbName || "", school_timetable_id: node.id }; setCurrentWorkerNode({ id: node.id, label: node.label, title: node.label, node_storage_path: node.node_storage_path || "", type: "UserTeacherTimetable", nodeData }); } } catch (error2) { setError(error2 instanceof Error ? error2.message : "Failed to navigate to lesson"); } finally { setIsLoading(false); } }; return /* @__PURE__ */ jsxRuntimeExports.jsx(NeoUserContext.Provider, { value: { userNode, calendarNode, workerNode, userDbName, workerDbName, isLoading, isInitialized, error, navigateToDay, navigateToWeek, navigateToMonth, navigateToYear, navigateToTimetable, navigateToJournal, navigateToPlanner, navigateToClass, navigateToLesson, currentCalendarNode, currentWorkerNode, calendarStructure, workerStructure }, children }); }; const useNeoUser = () => reactExports.useContext(NeoUserContext); class SchoolNeoDBService { static async createSchools() { logger.warn("school-service", "πŸ“€ Creating schools using default config.yaml"); try { const response = await axios.post( "/database/entity/create-schools", {}, { headers: { "Content-Type": "application/json" } } ); if (response.data.status === "success" || response.data.status === "Accepted") { logger.info("school-service", "βœ… Schools successfully"); return { status: "success", message: "Schools created successfully" }; } throw new Error(response.data.message || "Creation failed"); } catch (err) { const error = err; logger.error("school-service", "❌ Failed to create school", { error: error.message, details: error.response?.data }); throw error; } } static async getSchoolNode(schoolDbName) { logger.debug("school-service", "πŸ”„ Fetching school node", { schoolDbName }); try { const response = await axios.get(`/database/tools/get-default-node/school?db_name=${schoolDbName}`); if (response.data?.status === "success" && response.data.node) { logger.info("school-service", "βœ… School node fetched successfully"); return response.data.node; } logger.warn("school-service", "⚠️ No school node found"); return null; } catch (error) { if (error instanceof AxiosError && error.response?.status === 404) { logger.warn("school-service", "⚠️ School node not found (404)", { schoolDbName }); return null; } logger.error("school-service", "❌ Failed to fetch school node:", error); throw error; } } } const NeoInstituteContext = reactExports.createContext({ schoolNode: null, isLoading: true, isInitialized: false, error: null }); const NeoInstituteProvider = ({ children }) => { const { user } = useAuth(); const { profile, isInitialized: isUserInitialized } = useUser(); const [schoolNode, setSchoolNode] = reactExports.useState(null); const [isLoading, setIsLoading] = reactExports.useState(true); const [isInitialized, setIsInitialized] = reactExports.useState(false); const [error, setError] = reactExports.useState(null); reactExports.useEffect(() => { if (!isUserInitialized) { logger.debug("neo-institute-context", "⏳ Waiting for user initialization..."); return; } if (!profile || !profile.school_db_name) { setIsLoading(false); setIsInitialized(true); return; } const loadSchoolNode = async () => { try { setIsLoading(true); logger.debug("neo-institute-context", "πŸ”„ Loading school node", { schoolDbName: profile.school_db_name, userEmail: user?.email }); const node = await SchoolNeoDBService.getSchoolNode(profile.school_db_name); if (node) { setSchoolNode(node); logger.debug("neo-institute-context", "βœ… School node loaded", { schoolId: node.uuid_string, dbName: profile.school_db_name }); } else { logger.warn("neo-institute-context", "⚠️ No school node found"); } } catch (error2) { const errorMessage = error2 instanceof Error ? error2.message : "Failed to load school node"; logger.error("neo-institute-context", "❌ Failed to load school node", { error: errorMessage, schoolDbName: profile.school_db_name }); setError(errorMessage); } finally { setIsLoading(false); setIsInitialized(true); } }; loadSchoolNode(); }, [user?.email, profile, isUserInitialized]); return /* @__PURE__ */ jsxRuntimeExports.jsx(NeoInstituteContext.Provider, { value: { schoolNode, isLoading, isInitialized, error }, children }); }; const useNeoInstitute = () => reactExports.useContext(NeoInstituteContext); var Menu = {}; var interopRequireDefault = {exports: {}}; (function (module) { function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } module.exports = _interopRequireDefault, module.exports.__esModule = true, module.exports["default"] = module.exports; } (interopRequireDefault)); var interopRequireDefaultExports = interopRequireDefault.exports; var createSvgIcon = {}; const require$$0 = /*@__PURE__*/getAugmentedNamespace(utils$7); var hasRequiredCreateSvgIcon; function requireCreateSvgIcon () { if (hasRequiredCreateSvgIcon) return createSvgIcon; hasRequiredCreateSvgIcon = 1; (function (exports) { 'use client'; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "default", { enumerable: true, get: function () { return _utils.createSvgIcon; } }); var _utils = require$$0; } (createSvgIcon)); return createSvgIcon; } var _interopRequireDefault$d = interopRequireDefaultExports; Object.defineProperty(Menu, "__esModule", { value: true }); var default_1$d = Menu.default = void 0; var _createSvgIcon$d = _interopRequireDefault$d(requireCreateSvgIcon()); var _jsxRuntime$d = jsxRuntimeExports; var _default$d = (0, _createSvgIcon$d.default)( /*#__PURE__*/(0, _jsxRuntime$d.jsx)("path", { d: "M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" }), 'Menu'); default_1$d = Menu.default = _default$d; const NAVIGATION_CONTEXTS = { // Personal Contexts profile: { id: "profile", icon: "Person", label: "User Profile", description: "Personal workspace and settings", defaultNodeId: "user-root", views: [ { id: "overview", icon: "Dashboard", label: "Overview", description: "View your profile overview" }, { id: "settings", icon: "Settings", label: "Settings", description: "Manage your preferences" }, { id: "history", icon: "History", label: "History", description: "View your activity history" }, { id: "journal", icon: "Book", label: "Journal", description: "Your personal journal" }, { id: "planner", icon: "Event", label: "Planner", description: "Plan your activities" } ] }, calendar: { id: "calendar", icon: "CalendarToday", label: "Calendar", description: "Calendar navigation and events", defaultNodeId: "calendar-root", views: [ { id: "overview", icon: "Dashboard", label: "Overview", description: "Calendar overview" }, { id: "day", icon: "Today", label: "Day View", description: "Navigate by day" }, { id: "week", icon: "ViewWeek", label: "Week View", description: "Navigate by week" }, { id: "month", icon: "DateRange", label: "Month View", description: "Navigate by month" }, { id: "year", icon: "Event", label: "Year View", description: "Navigate by year" } ] }, teaching: { id: "teaching", icon: "School", label: "Teaching", description: "Teaching workspace", defaultNodeId: "teacher-root", views: [ { id: "overview", icon: "Dashboard", label: "Overview", description: "Teaching overview" }, { id: "timetable", icon: "Schedule", label: "Timetable", description: "View your teaching schedule" }, { id: "classes", icon: "Class", label: "Classes", description: "Manage your classes" }, { id: "lessons", icon: "Book", label: "Lessons", description: "Plan and view lessons" }, { id: "journal", icon: "Book", label: "Journal", description: "Your teaching journal" }, { id: "planner", icon: "Event", label: "Planner", description: "Plan your teaching activities" } ] }, // Institutional Contexts school: { id: "school", icon: "Business", label: "School", description: "School management", defaultNodeId: "school-root", views: [ { id: "overview", icon: "Dashboard", label: "Overview", description: "School overview" }, { id: "departments", icon: "AccountTree", label: "Departments", description: "View departments" }, { id: "staff", icon: "People", label: "Staff", description: "Staff directory" } ] }, department: { id: "department", icon: "AccountTree", label: "Department", description: "Department management", defaultNodeId: "department-root", views: [ { id: "overview", icon: "Dashboard", label: "Overview", description: "Department overview" }, { id: "teachers", icon: "People", label: "Teachers", description: "Department teachers" }, { id: "subjects", icon: "Subject", label: "Subjects", description: "Department subjects" } ] }, class: { id: "class", icon: "Class", label: "Class", description: "Class management", defaultNodeId: "class-root", views: [ { id: "overview", icon: "Dashboard", label: "Overview", description: "Class overview" }, { id: "students", icon: "People", label: "Students", description: "Class students" }, { id: "timetable", icon: "Schedule", label: "Timetable", description: "Class schedule" } ] } }; const NavigationRoot = styled(Box)` display: flex; align-items: center; gap: 8px; height: 100%; overflow: hidden; `; const NavigationControls = styled(Box)` display: flex; align-items: center; gap: 4px; `; const ContextToggleContainer = styled(Box)(({ theme }) => ({ display: "flex", alignItems: "center", backgroundColor: theme.palette.action.hover, borderRadius: theme.shape.borderRadius, padding: theme.spacing(0.5), gap: theme.spacing(0.5), "& .button-label": { "@media (max-width: 500px)": { display: "none" } } })); const ContextToggleButton = styled(Button, { shouldForwardProp: (prop) => prop !== "active" })(({ theme, active }) => ({ minWidth: 0, padding: theme.spacing(0.5, 1.5), borderRadius: theme.shape.borderRadius, backgroundColor: active ? theme.palette.primary.main : "transparent", color: active ? theme.palette.primary.contrastText : theme.palette.text.primary, textTransform: "none", transition: theme.transitions.create(["background-color", "color"], { duration: theme.transitions.duration.shorter }), "&:hover": { backgroundColor: active ? theme.palette.primary.dark : theme.palette.action.hover }, "@media (max-width: 500px)": { padding: theme.spacing(0.5) } })); const GraphNavigator = () => { const { context, switchContext, goBack, goForward, isLoading } = useNavigationStore(); const { userDbName, workerDbName, isInitialized: isNeoUserInitialized } = useNeoUser(); const [contextMenuAnchor, setContextMenuAnchor] = reactExports.useState(null); const [historyMenuAnchor, setHistoryMenuAnchor] = reactExports.useState(null); const rootRef = reactExports.useRef(null); const [availableWidth, setAvailableWidth] = reactExports.useState(0); reactExports.useEffect(() => { const calculateAvailableSpace = () => { if (!rootRef.current) return; const header = rootRef.current.closest(".MuiToolbar-root"); if (!header) return; const title = header.querySelector(".app-title"); const menu = header.querySelector(".menu-button"); if (!title || !menu) return; const headerWidth = header.clientWidth; const titleWidth = title.clientWidth; const menuWidth = menu.clientWidth; const padding = 48; const newAvailableWidth = headerWidth - titleWidth - menuWidth - padding; console.log("Available width:", newAvailableWidth); setAvailableWidth(newAvailableWidth); }; const resizeObserver = new ResizeObserver(() => { window.requestAnimationFrame(calculateAvailableSpace); }); if (rootRef.current) { const header = rootRef.current.closest(".MuiToolbar-root"); if (header) { resizeObserver.observe(header); resizeObserver.observe(rootRef.current); } } calculateAvailableSpace(); return () => { resizeObserver.disconnect(); }; }, []); const getVisibility = () => { if (availableWidth < 300) { return { navigation: false, contextLabel: true, // Keep context label visible longer toggleLabels: false }; } else if (availableWidth < 450) { return { navigation: false, contextLabel: true, // Keep context label visible toggleLabels: true }; } else if (availableWidth < 600) { return { navigation: true, contextLabel: true, toggleLabels: true }; } return { navigation: true, contextLabel: true, toggleLabels: true }; }; const visibility = getVisibility(); const handleHistoryClick = (event) => { setHistoryMenuAnchor(event.currentTarget); }; const handleHistoryClose = () => { setHistoryMenuAnchor(null); }; const handleHistoryItemClick = (index) => { const { currentIndex } = context.history; const steps = index - currentIndex; if (steps < 0) { for (let i = 0; i < -steps; i++) { goBack(); } } else if (steps > 0) { for (let i = 0; i < steps; i++) { goForward(); } } handleHistoryClose(); }; const handleContextChange = reactExports.useCallback(async (newContext) => { try { if (["school", "department", "class"].includes(newContext) && !workerDbName) { logger.error("navigation", "❌ Cannot switch to institute context: missing worker database"); return; } if (["profile", "calendar", "teaching"].includes(newContext) && !userDbName) { logger.error("navigation", "❌ Cannot switch to profile context: missing user database"); return; } logger.debug("navigation", "πŸ”„ Changing main context", { from: context.main, to: newContext, userDbName, workerDbName }); const defaultView = getDefaultViewForContext(newContext); await switchContext({ main: ["profile", "calendar", "teaching"].includes(newContext) ? "profile" : "institute", base: newContext, extended: defaultView, skipBaseContextLoad: false }, userDbName, workerDbName); } catch (error) { logger.error("navigation", "❌ Failed to change context:", error); } }, [context.main, switchContext, userDbName, workerDbName]); const getDefaultViewForContext = (context2) => { switch (context2) { case "calendar": return "overview"; case "teaching": return "overview"; case "school": return "overview"; case "department": return "overview"; case "class": return "overview"; default: return "overview"; } }; const handleContextMenu = (event) => { setContextMenuAnchor(event.currentTarget); }; const handleContextSelect = reactExports.useCallback(async (context2) => { setContextMenuAnchor(null); try { const contextDef = NAVIGATION_CONTEXTS[context2]; const defaultExtended = contextDef?.views[0]?.id; await switchContext({ base: context2, extended: defaultExtended }, userDbName, workerDbName); } catch (error) { logger.error("navigation", "❌ Failed to select context:", error); } }, [switchContext, userDbName, workerDbName]); const getContextItems = reactExports.useCallback(() => { if (context.main === "profile") { return [ { id: "profile", label: "Profile", icon: AccountCircleIcon }, { id: "calendar", label: "Calendar", icon: CalendarIcon }, { id: "teaching", label: "Teaching", icon: TeacherIcon } ]; } else { return [ { id: "school", label: "School", icon: BusinessIcon }, { id: "department", label: "Department", icon: GraphIcon }, { id: "class", label: "Class", icon: ClassIcon } ]; } }, [context.main]); const getContextIcon = reactExports.useCallback((contextType) => { switch (contextType) { case "profile": return /* @__PURE__ */ jsxRuntimeExports.jsx(AccountCircleIcon, {}); case "calendar": return /* @__PURE__ */ jsxRuntimeExports.jsx(CalendarIcon, {}); case "teaching": return /* @__PURE__ */ jsxRuntimeExports.jsx(TeacherIcon, {}); case "school": return /* @__PURE__ */ jsxRuntimeExports.jsx(BusinessIcon, {}); case "department": return /* @__PURE__ */ jsxRuntimeExports.jsx(GraphIcon, {}); case "class": return /* @__PURE__ */ jsxRuntimeExports.jsx(ClassIcon, {}); default: return /* @__PURE__ */ jsxRuntimeExports.jsx(AccountCircleIcon, {}); } }, []); const isDisabled = !isNeoUserInitialized || isLoading; const { history } = context; const canGoBack = history.currentIndex > 0; const canGoForward = history.currentIndex < history.nodes.length - 1; return /* @__PURE__ */ jsxRuntimeExports.jsxs(NavigationRoot, { ref: rootRef, children: [ /* @__PURE__ */ jsxRuntimeExports.jsxs(NavigationControls, { sx: { display: visibility.navigation ? "flex" : "none" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Back", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsx( IconButton, { onClick: goBack, disabled: !canGoBack || isDisabled, size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowBackIcon, { fontSize: "small" }) } ) }) }), /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "History", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsx( IconButton, { onClick: handleHistoryClick, disabled: !history.nodes.length || isDisabled, size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(HistoryIcon, { fontSize: "small" }) } ) }) }), /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Forward", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsx( IconButton, { onClick: goForward, disabled: !canGoForward || isDisabled, size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowForwardIcon, { fontSize: "small" }) } ) }) }) ] }), /* @__PURE__ */ jsxRuntimeExports.jsx( Menu$1, { anchorEl: historyMenuAnchor, open: Boolean(historyMenuAnchor), onClose: handleHistoryClose, anchorOrigin: { vertical: "bottom", horizontal: "center" }, transformOrigin: { vertical: "top", horizontal: "center" }, children: history.nodes.map((node, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs( MenuItem, { onClick: () => handleHistoryItemClick(index), selected: index === history.currentIndex, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: getContextIcon(node.type) }), /* @__PURE__ */ jsxRuntimeExports.jsx( ListItemText, { primary: node.label || node.id, secondary: node.type } ) ] }, `${node.id}-${index}` )) } ), /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextToggleContainer, { children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( ContextToggleButton, { active: context.main === "profile", onClick: () => handleContextChange("profile"), startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(StudentIcon, {}), disabled: isDisabled || !userDbName, children: visibility.toggleLabels && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "button-label", children: "Profile" }) } ), /* @__PURE__ */ jsxRuntimeExports.jsx( ContextToggleButton, { active: context.main === "institute", onClick: () => handleContextChange("school"), startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(TeacherIcon, {}), disabled: isDisabled || !workerDbName, children: visibility.toggleLabels && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "button-label", children: "Institute" }) } ) ] }), /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: context.base, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs( Button, { onClick: handleContextMenu, disabled: isDisabled, sx: { minWidth: 0, p: 0.5, color: "text.primary", "&:hover": { bgcolor: "action.hover" } }, children: [ getContextIcon(context.base), visibility.contextLabel && /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { ml: 1 }, children: context.base }), /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandMoreIcon, { sx: { ml: visibility.contextLabel ? 0.5 : 0 } }) ] } ) }) }) }), /* @__PURE__ */ jsxRuntimeExports.jsx( Menu$1, { anchorEl: contextMenuAnchor, open: Boolean(contextMenuAnchor), onClose: () => setContextMenuAnchor(null), children: getContextItems().map((item) => /* @__PURE__ */ jsxRuntimeExports.jsxs( MenuItem, { onClick: () => handleContextSelect(item.id), disabled: isDisabled, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(item.icon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: item.label }) ] }, item.id )) } ) ] }); }; const Header = () => { const theme = useTheme(); const navigate = useNavigate(); const location = useLocation(); const { user, signOut } = useAuth(); const [anchorEl, setAnchorEl] = reactExports.useState(null); const [isAuthenticated, setIsAuthenticated] = reactExports.useState(!!user); const isAdmin = user?.email === "admin@classroomcopilot.ai"; const showGraphNavigation = location.pathname === "/single-player"; reactExports.useEffect(() => { const newAuthState = !!user; setIsAuthenticated(newAuthState); logger.debug("user-context", "πŸ”„ User state changed in header", { hasUser: newAuthState, userId: user?.id, userEmail: user?.email, userState: newAuthState ? "logged-in" : "logged-out", isAdmin }); }, [user, isAdmin]); const handleMenuOpen = (event) => { setAnchorEl(event.currentTarget); }; const handleMenuClose = () => { setAnchorEl(null); }; const handleNavigation = (path) => { navigate(path); handleMenuClose(); }; const handleSignupNavigation = (role) => { navigate("/signup", { state: { role } }); handleMenuClose(); }; const handleSignOut = async () => { try { logger.debug("auth-service", "πŸ”„ Signing out user", { userId: user?.id }); await signOut(); setIsAuthenticated(false); setAnchorEl(null); logger.debug("auth-service", "βœ… User signed out"); } catch (error) { logger.error("auth-service", "❌ Error signing out:", error); console.error("Error signing out:", error); } }; return /* @__PURE__ */ jsxRuntimeExports.jsx( AppBar, { position: "fixed", sx: { height: `${HEADER_HEIGHT}px`, bgcolor: theme.palette.background.paper, color: theme.palette.text.primary, boxShadow: 1 }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Toolbar$1, { sx: { display: "flex", justifyContent: "space-between", minHeight: `${HEADER_HEIGHT}px !important`, height: `${HEADER_HEIGHT}px`, gap: 2, px: { xs: 1, sm: 2 } }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { display: "flex", alignItems: "center", gap: 2, minWidth: { xs: "auto", sm: "200px" } }, children: /* @__PURE__ */ jsxRuntimeExports.jsx( Typography, { variant: "h6", component: "div", className: "app-title", sx: { cursor: "pointer", color: theme.palette.text.primary, "&:hover": { color: theme.palette.primary.main }, fontSize: { xs: "1rem", sm: "1.25rem" } }, onClick: () => navigate(isAuthenticated ? "/single-player" : "/"), children: "ClassroomCopilot" } ) }), /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { position: "absolute", left: "50%", transform: "translateX(-50%)", display: "flex", justifyContent: "center", visibility: showGraphNavigation ? "visible" : "hidden", width: { xs: "calc(100% - 160px)", // More space for menu and title sm: "calc(100% - 200px)", // Standard spacing md: "auto" // Full width on medium and up }, maxWidth: "800px", "& .navigation-controls": { display: { xs: "none", sm: "flex" } }, "& .context-section": { display: { xs: "none", md: "flex" } }, "& .context-toggle": { display: "flex" // Always show the profile/institute toggle } }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(GraphNavigator, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", justifyContent: "flex-end", minWidth: { xs: "auto", sm: "200px" }, ml: "auto" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( IconButton, { className: "menu-button", color: "inherit", onClick: handleMenuOpen, edge: "end", sx: { "&:hover": { bgcolor: theme.palette.action.hover } }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(default_1$d, {}) } ), /* @__PURE__ */ jsxRuntimeExports.jsx( Menu$1, { anchorEl, open: Boolean(anchorEl), onClose: handleMenuClose, slotProps: { paper: { elevation: 3, sx: { bgcolor: theme.palette.background.paper, color: theme.palette.text.primary, minWidth: "240px" } } }, children: isAuthenticated ? [ // Development Tools Section /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/tldraw-dev"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TLDrawDevIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "TLDraw Dev" }) ] }, "tldraw"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/dev"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DevToolsIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Dev Tools" }) ] }, "dev"), /* @__PURE__ */ jsxRuntimeExports.jsx(Divider, {}, "dev-divider"), // Main Features Section /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/multiplayer"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(MultiplayerIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Multiplayer" }) ] }, "multiplayer"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/calendar"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CalendarIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Calendar" }) ] }, "calendar"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/teacher-planner"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExamIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Teacher Planner" }) ] }, "planner"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/exam-marker"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExamMarkerIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Exam Marker" }) ] }, "exam"), /* @__PURE__ */ jsxRuntimeExports.jsx(Divider, {}, "features-divider"), // Utilities Section /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/settings"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Settings" }) ] }, "settings"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/search"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SearchIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Search" }) ] }, "search"), // Admin Section ...isAdmin ? [ /* @__PURE__ */ jsxRuntimeExports.jsx(Divider, {}, "admin-divider"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/admin"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(AdminIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Admin Dashboard" }) ] }, "admin") ] : [], // Authentication Section /* @__PURE__ */ jsxRuntimeExports.jsx(Divider, {}, "auth-divider"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: handleSignOut, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(LogoutIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Sign Out" }) ] }, "signout") ] : [ // Authentication Section for Non-authenticated Users /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleNavigation("/login"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoginIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemText, { primary: "Sign In" }) ] }, "signin"), /* @__PURE__ */ jsxRuntimeExports.jsx(Divider, {}, "signup-divider"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleSignupNavigation("teacher"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TeacherIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx( ListItemText, { primary: "Sign up as Teacher", secondary: "Create a teacher account" } ) ] }, "teacher-signup"), /* @__PURE__ */ jsxRuntimeExports.jsxs(MenuItem, { onClick: () => handleSignupNavigation("student"), children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(ListItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(StudentIcon, {}) }), /* @__PURE__ */ jsxRuntimeExports.jsx( ListItemText, { primary: "Sign up as Student", secondary: "Create a student account" } ) ] }, "student-signup") ] } ) ] }) ] }) } ); }; const HEADER_HEIGHT = 40; const Layout = ({ children }) => { return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(Header, {}), /* @__PURE__ */ jsxRuntimeExports.jsx("main", { className: "main-content", style: { paddingTop: `${HEADER_HEIGHT}px`, height: "100vh", width: "100%" }, children }) ] }); }; const EmailLoginForm = ({ role, onSubmit }) => { const [email, setEmail] = reactExports.useState(""); const [password, setPassword] = reactExports.useState(""); const [error, setError] = reactExports.useState(null); const [isLoading, setIsLoading] = reactExports.useState(false); const handleSubmit = async (e) => { e.preventDefault(); setError(null); setIsLoading(true); try { await onSubmit({ email, password, role }); } catch (err) { setError(err instanceof Error ? err.message : "Failed to login"); } finally { setIsLoading(false); } }; return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { component: "form", onSubmit: handleSubmit, sx: { width: "100%" }, children: [ error && /* @__PURE__ */ jsxRuntimeExports.jsx(Alert, { severity: "error", sx: { mb: 2 }, children: error }), /* @__PURE__ */ jsxRuntimeExports.jsx( TextField, { fullWidth: true, label: "Email", type: "email", value: email, onChange: (e) => setEmail(e.target.value), margin: "normal", required: true } ), /* @__PURE__ */ jsxRuntimeExports.jsx( TextField, { fullWidth: true, label: "Password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), margin: "normal", required: true } ), /* @__PURE__ */ jsxRuntimeExports.jsx( Button, { type: "submit", fullWidth: true, variant: "contained", color: "primary", disabled: isLoading, sx: { mt: 3 }, children: isLoading ? "Logging in..." : "Login" } ) ] }); }; const LoginPage = () => { const navigate = useNavigate(); const { user, signIn } = useAuth(); const [error, setError] = reactExports.useState(null); logger.debug("login-page", "πŸ” Login page loaded", { hasUser: !!user }); reactExports.useEffect(() => { if (user) { navigate("/single-player"); } }, [user, navigate]); const handleLogin = async (credentials) => { try { setError(null); await signIn(credentials.email, credentials.password); navigate("/single-player"); } catch (error2) { logger.error("login-page", "❌ Login failed", error2); setError(error2 instanceof Error ? error2.message : "Login failed"); throw error2; } }; if (user) { return null; } return /* @__PURE__ */ jsxRuntimeExports.jsxs( Container, { sx: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", gap: 4 }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "h2", component: "h1", gutterBottom: true, children: "ClassroomCopilot.ai" }), /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "h4", gutterBottom: true, children: "Login" }), error && /* @__PURE__ */ jsxRuntimeExports.jsx(Alert, { severity: "error", sx: { width: "100%", maxWidth: 400 }, children: error }), /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { width: "100%", maxWidth: 400 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx( EmailLoginForm, { role: "email_teacher", onSubmit: handleLogin } ) }) ] } ); }; const EmailSignupForm = ({ role, onSubmit }) => { const [email, setEmail] = reactExports.useState(""); const [password, setPassword] = reactExports.useState(""); const [confirmPassword, setConfirmPassword] = reactExports.useState(""); const [displayName, setDisplayName] = reactExports.useState(""); const [error, setError] = reactExports.useState(null); const [isLoading, setIsLoading] = reactExports.useState(false); const validateForm = () => { if (!email || !password || !confirmPassword || !displayName) { return "All fields are required"; } if (password !== confirmPassword) { return "Passwords do not match"; } if (password.length < 6) { return "Password must be at least 6 characters"; } if (!email.includes("@")) { return "Please enter a valid email address"; } return null; }; const handleSubmit = async (e) => { e.preventDefault(); const validationError = validateForm(); if (validationError) { setError(validationError); return; } setError(null); setIsLoading(true); try { logger.debug("email-signup-form", "πŸ”„ Submitting signup form", { email, role, hasDisplayName: !!displayName }); await onSubmit({ email, password, role }, displayName); } catch (err) { setError( err instanceof Error ? err.message : "An error occurred during signup" ); logger.error( "email-signup-form", "❌ Signup form submission failed", err ); } finally { setIsLoading(false); } }; return /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { component: "form", onSubmit: handleSubmit, noValidate: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { spacing: 2, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( TextField, { required: true, fullWidth: true, id: "displayName", label: "Display Name", name: "displayName", autoComplete: "name", value: displayName, onChange: (e) => setDisplayName(e.target.value), autoFocus: true } ), /* @__PURE__ */ jsxRuntimeExports.jsx( TextField, { required: true, fullWidth: true, id: "email", label: "Email Address", name: "email", autoComplete: "email", value: email, onChange: (e) => setEmail(e.target.value) } ), /* @__PURE__ */ jsxRuntimeExports.jsx( TextField, { required: true, fullWidth: true, name: "password", label: "Password", type: "password", id: "password", autoComplete: "new-password", value: password, onChange: (e) => setPassword(e.target.value) } ), /* @__PURE__ */ jsxRuntimeExports.jsx( TextField, { required: true, fullWidth: true, name: "confirmPassword", label: "Confirm Password", type: "password", id: "confirmPassword", autoComplete: "new-password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value) } ), error && /* @__PURE__ */ jsxRuntimeExports.jsx(Alert, { severity: "error", children: error }), /* @__PURE__ */ jsxRuntimeExports.jsx( Button, { type: "submit", fullWidth: true, variant: "contained", disabled: isLoading || !email || !password || !confirmPassword || !displayName, children: isLoading ? "Signing up..." : "Sign Up" } ) ] }) }); }; const DEV_SCHOOL_UUID = "kevlarai"; class NeoRegistrationService { static instance; constructor() { } static getInstance() { if (!NeoRegistrationService.instance) { NeoRegistrationService.instance = new NeoRegistrationService(); } return NeoRegistrationService.instance; } async registerNeo4JUser(user, username, role) { try { let schoolNode = null; if (role.includes("teacher") || role.includes("student")) { schoolNode = await this.fetchSchoolNode(DEV_SCHOOL_UUID); if (!schoolNode) { throw new Error("Failed to fetch required school node"); } } const formData = new FormData(); formData.append("user_id", user.id); formData.append("user_type", role); formData.append("user_name", username); formData.append("user_email", user.email || ""); if (schoolNode) { formData.append("school_uuid_string", schoolNode.uuid_string); formData.append("school_name", schoolNode.name); formData.append("school_website", schoolNode.website); formData.append("school_node_storage_path", schoolNode.node_storage_path); const workerData = role.includes("teacher") ? { teacher_code: username, teacher_name_formal: username, teacher_email: user.email } : { student_code: username, student_name_formal: username, student_email: user.email }; formData.append("worker_data", JSON.stringify(workerData)); } logger.debug("neo4j-service", "πŸ”„ Sending form data", { userId: user.id, userType: role, userName: username, userEmail: user.email, schoolNode: schoolNode ? { uuid_string: schoolNode.uuid_string, name: schoolNode.name } : null }); const response = await axios.post("/database/entity/create-user", formData, { headers: { "Content-Type": "multipart/form-data" } }); if (response.data.status !== "success") { throw new Error(`Failed to create user: ${JSON.stringify(response.data)}`); } const userNode = response.data.data.user_node; const workerNode = response.data.data.worker_node; if (response.data.data.calendar_nodes) { logger.debug("neo4j-service", "πŸ”„ Storing calendar data", { calendarNodes: response.data.data.calendar_nodes }); storageService.set(StorageKeys.CALENDAR_DATA, response.data.data.calendar_nodes); } userNode.worker_node_data = JSON.stringify(workerNode); await this.updateUserNeo4jDetails(user.id, userNode); logger.info("neo4j-service", "βœ… Neo4j user registration successful", { userId: user.id, nodeId: userNode.uuid_string, hasCalendar: !!response.data.data.calendar_nodes }); return userNode; } catch (error) { logger.error("neo4j-service", "❌ Neo4j user registration failed", error); throw error; } } async updateUserNeo4jDetails(userId, userNode) { const { error } = await supabase.from("profiles").update({ metadata: { ...userNode }, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", userId); if (error) { logger.error("neo4j-service", "❌ Failed to update Neo4j details:", error); throw error; } } async fetchSchoolNode(schoolUrn) { logger.debug("neo4j-service", "πŸ”„ Fetching school node", { schoolUrn }); try { const response = await axios.get(`/database/tools/get-school-node?school_urn=${schoolUrn}`); if (response.data?.status === "success" && response.data.school_node) { logger.info("neo4j-service", "βœ… School node fetched successfully"); return response.data.school_node; } throw new Error("Failed to fetch school node: " + JSON.stringify(response.data)); } catch (error) { logger.error("neo4j-service", "❌ Failed to fetch school node:", error); throw error; } } } const neoRegistrationService = NeoRegistrationService.getInstance(); const REGISTRATION_SERVICE = "registration-service"; class RegistrationService { static instance; constructor() { } static getInstance() { if (!RegistrationService.instance) { RegistrationService.instance = new RegistrationService(); } return RegistrationService.instance; } async register(credentials, displayName) { try { logger.debug(REGISTRATION_SERVICE, "πŸ”„ Starting registration", { email: credentials.email, role: credentials.role, hasDisplayName: !!displayName }); const username = formatEmailForDatabase(credentials.email); const { data: authData, error: signUpError } = await supabase.auth.signUp({ email: credentials.email, password: credentials.password, options: { data: { user_type: credentials.role, username, display_name: displayName } } }); if (signUpError) { logger.error(REGISTRATION_SERVICE, "❌ Supabase signup error", { error: signUpError }); throw signUpError; } if (!authData.user) { logger.error(REGISTRATION_SERVICE, "❌ No user data after registration"); throw new Error("No user data after registration"); } const ccUser = convertToCCUser(authData.user, authData.user.user_metadata); const { error: updateError } = await supabase.from("profiles").update({ user_type: credentials.role, username, display_name: displayName }).eq("id", authData.user.id).select().single(); if (updateError) { logger.error(REGISTRATION_SERVICE, "❌ Failed to update profile", updateError); throw updateError; } storageService.set(StorageKeys.IS_NEW_REGISTRATION, true); try { const userNode = await neoRegistrationService.registerNeo4JUser( ccUser, username, // Pass username for database operations credentials.role ); logger.info(REGISTRATION_SERVICE, "βœ… Registration successful with Neo4j setup", { userId: ccUser.id, hasUserNode: !!userNode }); return { user: ccUser, accessToken: authData.session?.access_token || null, userRole: credentials.role, message: "Registration successful" }; } catch (neo4jError) { logger.warn(REGISTRATION_SERVICE, "⚠️ Neo4j setup problem", { userId: ccUser.id, error: neo4jError }); return { user: ccUser, accessToken: authData.session?.access_token || null, userRole: credentials.role, message: "Registration successful - Neo4j setup pending" }; } } catch (error) { logger.error(REGISTRATION_SERVICE, "❌ Registration failed:", error); throw error; } } } RegistrationService.getInstance(); var Microsoft = {}; var _interopRequireDefault$c = interopRequireDefaultExports; Object.defineProperty(Microsoft, "__esModule", { value: true }); var default_1$c = Microsoft.default = void 0; _interopRequireWildcard(reactExports); var _createSvgIcon$c = _interopRequireDefault$c(requireCreateSvgIcon()); var _jsxRuntime$c = jsxRuntimeExports; function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } var _default$c = (0, _createSvgIcon$c.default)( /*#__PURE__*/(0, _jsxRuntime$c.jsx)("path", { d: "M2 3h9v9H2V3m9 19H2v-9h9v9M21 3v9h-9V3h9m0 19h-9v-9h9v9Z" }), 'Microsoft'); default_1$c = Microsoft.default = _default$c; const SignupPage = () => { const location = useLocation(); const navigate = useNavigate(); const { user } = useAuth(); const registrationService = RegistrationService.getInstance(); const { role = "teacher" } = location.state || {}; const roleDisplay = role === "teacher" ? "Teacher" : "Student"; logger.debug("signup-page", "πŸ” Signup page loaded", { role, hasUser: !!user }); reactExports.useEffect(() => { if (user) { navigate("/single-player"); } }, [user, navigate]); const handleSignup = async (credentials, displayName) => { try { const result = await registrationService.register( credentials, displayName ); if (result.user) { navigate("/single-player"); } } catch (error) { logger.error("signup-page", "❌ Registration failed", error); throw error; } }; const switchRole = () => { navigate("/signup", { state: { role: role === "teacher" ? "student" : "teacher" } }); }; if (user) { return null; } return /* @__PURE__ */ jsxRuntimeExports.jsxs( Container, { sx: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", gap: 4 }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "h2", component: "h1", gutterBottom: true, children: "ClassroomCopilot.ai" }), /* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "h4", gutterBottom: true, children: [ roleDisplay, " Sign Up" ] }), /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { width: "100%", maxWidth: 400 }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( Button, { fullWidth: true, variant: "outlined", startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(default_1$c, {}), onClick: () => { }, sx: { mb: 3 }, children: "Sign up with Microsoft" } ), /* @__PURE__ */ jsxRuntimeExports.jsx(Divider, { sx: { my: 2 }, children: "OR" }), /* @__PURE__ */ jsxRuntimeExports.jsx( EmailSignupForm, { role: `email_${role}`, onSubmit: handleSignup } ), /* @__PURE__ */ jsxRuntimeExports.jsx( Stack, { direction: "row", spacing: 2, justifyContent: "center", sx: { mt: 3 }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "text", onClick: switchRole, children: [ "Switch to ", role === "teacher" ? "Student" : "Teacher", " Sign Up" ] }) } ) ] }) ] } ); }; const CC_BASE_STYLE_CONSTANTS = { // Container styles CONTAINER: { borderRadius: "4px", borderWidth: "2px", boxShadow: "0 2px 4px var(--color-muted-1)" }, HEADER: { height: 32, padding: 8}, CONTENT: { padding: 8, backgroundColor: "white" }, COLORS: { primary: "#3e6589", border: "#e2e8f0"}, // Minimum dimensions MIN_DIMENSIONS: { width: 100, height: 100 } }; const CC_CALENDAR_STYLE_CONSTANTS = { // Calendar event styles EVENT: { mainFrame: { backgroundColor: "transparent", padding: "0px", display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100%", borderRadius: "4px" }, title: { fontSize: "1.1em", fontWeight: "normal", textAlign: "center", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 1, padding: "0px 0px", width: "100%", letterSpacing: "0.02em", margin: "0px 0px" } } }; const CC_SLIDESHOW_STYLE_CONSTANTS = { DEFAULT_SLIDE_WIDTH: 1280, DEFAULT_SLIDE_HEIGHT: 720, SLIDE_HEADER_HEIGHT: 32, SLIDE_HEADER_PADDING: 8, SLIDE_CONTENT_PADDING: 0, SLIDE_BORDER_RADIUS: 4, SLIDE_BORDER_WIDTH: 1, SLIDE_SPACING: 16, SLIDE_COLORS: { background: "#ffffff", border: "#e2e8f0", text: "#ffffff", secondary: "#718096" } }; class CCBaseShapeUtil extends BaseBoxShapeUtil { indicator(shape) { return /* @__PURE__ */ jsxRuntimeExports.jsx( "rect", { width: shape.props.w, height: shape.props.h, fill: "none", rx: CC_BASE_STYLE_CONSTANTS.CONTAINER.borderRadius, stroke: CC_BASE_STYLE_CONSTANTS.COLORS.border, strokeWidth: CC_BASE_STYLE_CONSTANTS.CONTAINER.borderWidth } ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars getToolbarItems(shape) { return []; } onAfterCreate(shape) { logger.info("cc-base-shape-util", "onAfterCreate", shape); return shape; } component(shape) { const { props: { w, h, isLocked } } = shape; const toolbarItems = this.getToolbarItems(shape); return /* @__PURE__ */ jsxRuntimeExports.jsxs( HTMLContainer, { id: shape.id, style: { width: toDomPrecision(w), height: toDomPrecision(h), backgroundColor: shape.props.headerColor, borderRadius: CC_BASE_STYLE_CONSTANTS.CONTAINER.borderRadius, boxShadow: CC_BASE_STYLE_CONSTANTS.CONTAINER.boxShadow, overflow: "hidden", position: "relative" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { style: { backgroundColor: shape.props.headerColor, padding: CC_BASE_STYLE_CONSTANTS.HEADER.padding, height: CC_BASE_STYLE_CONSTANTS.HEADER.height, display: "flex", justifyContent: "space-between", alignItems: "center", cursor: isLocked ? "not-allowed" : "move", pointerEvents: "all", position: "relative", zIndex: 1 }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "white", fontWeight: "bold" }, children: shape.props.title }), /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: "4px", alignItems: "center", pointerEvents: "all" }, children: [ toolbarItems.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { title: item.label, onClick: (e) => { logger.info("cc-base-shape-util", "toolbar item clicked", item.id); e.preventDefault(); e.stopPropagation(); item.onClick(e, shape); }, onPointerDown: (e) => { logger.info("cc-base-shape-util", "toolbar item pointer down", item.id); e.preventDefault(); e.stopPropagation(); }, style: { background: "transparent", border: "none", padding: "4px", cursor: "pointer", color: "white", opacity: item.isActive ? 1 : 0.7, display: "flex", alignItems: "center", justifyContent: "center", pointerEvents: "all", fontSize: "16px", width: "24px", height: "24px", zIndex: 100, userSelect: "none", position: "relative", touchAction: "none" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { pointerEvents: "none" }, children: item.icon }) }, item.id )), isLocked && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "white" }, children: "πŸ”’" }) ] }) ] } ), /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { style: { position: "absolute", top: CC_BASE_STYLE_CONSTANTS.HEADER.height, left: 0, right: 0, bottom: 0, overflow: "auto", padding: CC_BASE_STYLE_CONSTANTS.CONTENT.padding, backgroundColor: shape.props.backgroundColor }, children: this.renderContent(shape) } ) ] } ); } } const baseShapeProps = { title: string, w: number, h: number, headerColor: string, backgroundColor: string, isLocked: boolean }; const ccShapeProps = { calendar: { ...baseShapeProps, date: string, selectedDate: string, view: string, events: arrayOf(object$1({ id: string, title: string, start: string, end: string, groupId: string.optional(), extendedProps: object$1({ subjectClass: string, color: string, periodCode: string, node_storage_path: string.optional() }) })) }, liveTranscription: { ...baseShapeProps, isRecording: boolean, segments: arrayOf(object$1({ id: string, text: string, completed: boolean, start: string, end: string })), currentSegment: object$1({ id: string, text: string, completed: boolean, start: string, end: string }).optional(), lastProcessedSegment: string.optional() }, settings: { ...baseShapeProps, userEmail: string, user_role: string, isTeacher: boolean }, slideshow: { ...baseShapeProps, currentSlideIndex: number, slidePattern: string, numSlides: number, slides: arrayOf(object$1({ imageData: string, meta: object$1({ text: string, format: string }) })).optional() }, slide: { ...baseShapeProps, imageData: string, meta: object$1({ text: string, format: string }) }, "cc-youtube-embed": { ...baseShapeProps, video_url: string, transcript: arrayOf(object$1({ start: number, duration: number, text: string })), transcriptVisible: boolean }, search: { ...baseShapeProps, query: string, results: arrayOf(object$1({ title: string, url: string, content: string })), isSearching: boolean }, webBrowser: { ...baseShapeProps, url: string, history: arrayOf(string), currentHistoryIndex: number, isLoading: boolean } }; ({ "cc-slide-layout": { isMovingWithParent: boolean.optional(), placeholder: boolean.optional()} }); const getDefaultCCBaseProps = () => ({ title: "Base Shape", w: 100, h: 100, headerColor: "#3e6589", backgroundColor: "#ffffff", isLocked: false }); const getDefaultCCCalendarProps = () => ({ ...getDefaultCCBaseProps(), date: (/* @__PURE__ */ new Date()).toISOString(), selectedDate: (/* @__PURE__ */ new Date()).toISOString(), view: "timeGridWeek", events: [] }); const getDefaultCCLiveTranscriptionProps = () => ({ ...getDefaultCCBaseProps(), isRecording: false, segments: [], currentSegment: void 0, lastProcessedSegment: void 0 }); const getDefaultCCSettingsProps = () => ({ ...getDefaultCCBaseProps(), userEmail: "", user_role: "", isTeacher: false }); function getDefaultCCSlideShowProps() { const baseWidth = 1280; const baseHeight = 720; const totalHeight = baseHeight + CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_HEADER_HEIGHT + // Slideshow's own header CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_SPACING * 2 + // Top and bottom spacing CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_CONTENT_PADDING; return { title: "Slideshow", w: baseWidth, h: totalHeight, headerColor: "#3e6589", backgroundColor: "#0f0f0f", isLocked: false, currentSlideIndex: 0, slidePattern: "horizontal", numSlides: 3, slides: [] }; } function getDefaultCCSlideProps() { const baseWidth = 1280; const baseHeight = 720; const totalHeight = baseHeight + CC_BASE_STYLE_CONSTANTS.HEADER.height; return { title: "Slide", w: baseWidth, h: totalHeight, headerColor: "#3e6589", backgroundColor: "#0f0f0f", isLocked: false, imageData: "", meta: { text: "", format: "markdown" } }; } function getDefaultCCYoutubeEmbedProps() { const videoHeight = 450; const totalHeight = videoHeight + CC_BASE_STYLE_CONSTANTS.HEADER.height + CC_BASE_STYLE_CONSTANTS.CONTENT.padding * 2; return { ...getDefaultCCBaseProps(), title: "YouTube Video", w: 800, h: totalHeight, headerColor: "#ff0000", backgroundColor: "#0f0f0f", isLocked: false, video_url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", transcript: [], transcriptVisible: false }; } const getDefaultCCSearchProps = () => ({ ...getDefaultCCBaseProps(), w: 400, h: 500, title: "Search", headerColor: "#1a73e8", backgroundColor: "#ffffff", query: "", results: [], isSearching: false }); const getDefaultCCWebBrowserProps = () => ({ ...getDefaultCCBaseProps(), title: "Web Browser", w: 800, h: 600, headerColor: "#1a73e8", backgroundColor: "#ffffff", url: "", history: [], currentHistoryIndex: -1, isLoading: false }); const ccShapeMigrations = { calendar: { firstVersion: 1, currentVersion: 1, migrators: { 1: { up: (record) => { if (record.typeName !== "shape") return record; const shape = record; if (shape.type !== "cc-calendar") return record; return { ...shape, props: { ...getDefaultCCCalendarProps(), ...shape.props } }; }, down: (record) => { return record; } } } }, liveTranscription: { firstVersion: 1, currentVersion: 1, migrators: { 1: { up: (record) => { if (record.typeName !== "shape") return record; const shape = record; if (shape.type !== "cc-live-transcription") return record; return { ...shape, props: { ...getDefaultCCLiveTranscriptionProps(), ...shape.props } }; }, down: (record) => { return record; } } } }, settings: { firstVersion: 1, currentVersion: 1, migrators: { 1: { up: (record) => { if (record.typeName !== "shape") return record; const shape = record; if (shape.type !== "cc-settings") return record; return { ...shape, props: { ...getDefaultCCSettingsProps(), ...shape.props } }; }, down: (record) => { return record; } } } }, slideshow: { firstVersion: 1, currentVersion: 1, migrators: { 1: { up: (record) => { if (record.typeName !== "shape") return record; const shape = record; if (shape.type !== "cc-slideshow") return record; return { ...shape, props: { ...getDefaultCCSlideShowProps(), ...shape.props } }; }, down: (record) => { return record; } } } }, slide: { firstVersion: 1, currentVersion: 1, migrators: { 1: { up: (record) => { if (record.typeName !== "shape") return record; const shape = record; if (shape.type !== "cc-slide") return record; return { ...shape, props: { ...getDefaultCCSlideProps(), ...shape.props } }; }, down: (record) => { return record; } } } }, search: { firstVersion: 1, currentVersion: 1, migrators: { 1: { up: (record) => { if (record.typeName !== "shape") return record; const shape = record; if (shape.type !== "cc-search") return record; return { ...shape, props: { ...getDefaultCCSearchProps(), ...shape.props } }; }, down: (record) => { return record; } } } }, webBrowser: { firstVersion: 1, currentVersion: 1, migrators: { 1: { up: (record) => { if (record.typeName !== "shape") return record; const shape = record; if (shape.type !== "cc-web-browser") return record; return { ...shape, props: { ...getDefaultCCWebBrowserProps(), ...shape.props } }; }, down: (record) => { return record; } } } } }; class CCSlideShowShapeUtil extends CCBaseShapeUtil { static type = "cc-slideshow"; static props = ccShapeProps.slideshow; static migrations = ccShapeMigrations.slideshow; static styles = { color: DefaultColorStyle, dash: DefaultDashStyle, size: DefaultSizeStyle }; getDefaultProps() { return getDefaultCCSlideShowProps(); } canResize = () => false; isAspectRatioLocked = () => true; hideResizeHandles = () => false; hideRotateHandle = () => false; canEdit = () => false; // eslint-disable-next-line @typescript-eslint/no-unused-vars canBind(args) { return true; } onBeforeCreate(shape) { return shape; } renderContent = () => { return /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}); }; } class CCSlideShapeUtil extends CCBaseShapeUtil { static type = "cc-slide"; static props = ccShapeProps.slide; static migrations = ccShapeMigrations.slide; static styles = { color: DefaultColorStyle, dash: DefaultDashStyle, size: DefaultSizeStyle }; getDefaultProps() { return getDefaultCCSlideProps(); } canResize = () => false; isAspectRatioLocked = () => true; hideResizeHandles = () => true; hideRotateHandle = () => true; canEdit = () => false; canBind(args) { return args.fromShapeType === "cc-slideshow" && args.toShapeType === "cc-slide" && args.bindingType === "cc-slide-layout"; } getTargetSlideshow(shape, pageAnchor) { return this.editor.getShapeAtPoint(pageAnchor, { hitInside: true, filter: (otherShape) => this.editor.canBindShapes({ fromShape: otherShape, toShape: shape, binding: "cc-slide-layout" }) }); } getBindingIndexForPosition(shape, slideshow, pageAnchor) { const allBindings = this.editor.getBindingsFromShape(slideshow, "cc-slide-layout").filter((b) => !b.props.placeholder || b.toId === shape.id).sort((a, b) => a.props.index > b.props.index ? 1 : -1); const spacing = CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_SPACING; const headerHeight = CC_BASE_STYLE_CONSTANTS.HEADER.height; const contentPadding = CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_CONTENT_PADDING; let order; if (slideshow.props.slidePattern === "horizontal") { order = clamp$1( Math.round((pageAnchor.x - slideshow.x - spacing) / (shape.props.w + spacing)), 0, allBindings.length ); } else if (slideshow.props.slidePattern === "vertical") { order = clamp$1( Math.round((pageAnchor.y - slideshow.y - headerHeight - contentPadding - spacing) / (shape.props.h + spacing)), 0, allBindings.length ); } else if (slideshow.props.slidePattern === "grid") { const cols = Math.ceil(Math.sqrt(allBindings.length)); const col = clamp$1( Math.round((pageAnchor.x - slideshow.x - spacing) / (shape.props.w + spacing)), 0, cols ); const row = clamp$1( Math.round((pageAnchor.y - slideshow.y - headerHeight - contentPadding - spacing) / (shape.props.h + spacing)), 0, Math.ceil(allBindings.length / cols) ); order = clamp$1(row * cols + col, 0, allBindings.length); } else { order = 0; } const belowSib = allBindings[order - 1]; const aboveSib = allBindings[order]; if (belowSib?.toId === shape.id) { return belowSib.props.index; } else if (aboveSib?.toId === shape.id) { return aboveSib.props.index; } return getIndexBetween(belowSib?.props.index, aboveSib?.props.index); } onTranslateStart = (shape) => { const bindings = this.editor.getBindingsToShape(shape.id, "cc-slide-layout"); logger.debug("shape", "βœ… onTranslateStart", { shape, bindings, hasBindings: bindings.length > 0, bindingTypes: bindings.map((b) => ({ id: b.id, fromId: b.fromId, placeholder: b.props.placeholder, isMovingWithParent: b.props.isMovingWithParent })) }); this.editor.updateBindings( bindings.map((binding) => ({ ...binding, props: { ...binding.props, placeholder: true } })) ); }; onTranslate = (initial, current) => { const pageAnchor = this.editor.getShapePageTransform(current).applyToPoint({ x: current.props.w / 2, y: current.props.h / 2 }); const targetSlideshow = this.getTargetSlideshow(current, pageAnchor); const currentBindings = this.editor.getBindingsToShape(current.id, "cc-slide-layout"); const currentBinding = currentBindings[0]; const currentSlideshow = currentBinding ? this.editor.getShape(currentBinding.fromId) : void 0; logger.debug("shape", "βœ… onTranslate", { initial, current, hasTargetSlideshow: !!targetSlideshow, targetSlideshowId: targetSlideshow?.id, currentBindings: currentBindings.map((b) => ({ id: b.id, fromId: b.fromId, placeholder: b.props.placeholder, isMovingWithParent: b.props.isMovingWithParent })), isInSlideshow: targetSlideshow ? this.isSlideInSlideshow(current, targetSlideshow) : false }); if (currentBinding && currentSlideshow && !this.isSlideInSlideshow(current, currentSlideshow)) { logger.debug("shape", "βœ… onTranslate: Moving out of slideshow", { slideId: current.id, slideshowId: currentSlideshow.id }); this.editor.deleteBindings(currentBindings); return current; } if (!targetSlideshow) { return current; } const index = this.getBindingIndexForPosition(current, targetSlideshow, pageAnchor); if (currentBinding && currentBinding.fromId === targetSlideshow.id) { if (currentBinding.props.index !== index) { logger.debug("shape", "βœ… onTranslate: Updating binding index", { slideId: current.id, slideshowId: targetSlideshow.id, oldIndex: currentBinding.props.index, newIndex: index }); this.editor.updateBinding({ id: currentBinding.id, type: currentBinding.type, fromId: currentBinding.fromId, toId: currentBinding.toId, props: { ...currentBinding.props, index, isMovingWithParent: true } }); } } else if (this.isSlideInSlideshow(current, targetSlideshow)) { logger.debug("shape", "βœ… onTranslate: Creating new placeholder binding", { slideId: current.id, slideshowId: targetSlideshow.id, index }); this.editor.createBinding({ type: "cc-slide-layout", fromId: targetSlideshow.id, toId: current.id, props: { index, isMovingWithParent: true, placeholder: true } }); } return current; }; isSlideInSlideshow(slide, slideshow) { const slideCenter = this.editor.getShapePageTransform(slide).applyToPoint({ x: slide.props.w / 2, y: slide.props.h / 2 }); const slideshowBounds = this.editor.getShapeGeometry(slideshow).bounds; const padding = CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_SPACING / 4; return slideCenter.x >= slideshow.x + padding && slideCenter.x <= slideshow.x + slideshowBounds.width - padding && slideCenter.y >= slideshow.y + padding && slideCenter.y <= slideshow.y + slideshowBounds.height - padding; } onTranslateEnd = (shape) => { const pageAnchor = this.editor.getShapePageTransform(shape).applyToPoint({ x: shape.props.w / 2, y: shape.props.h / 2 }); const targetSlideshow = this.getTargetSlideshow(shape, pageAnchor); const bindings = this.editor.getBindingsToShape(shape.id, "cc-slide-layout"); logger.debug("shape", "βœ… onTranslateEnd", { shape, hasTargetSlideshow: !!targetSlideshow, targetSlideshowId: targetSlideshow?.id, bindings: bindings.map((b) => ({ id: b.id, fromId: b.fromId, placeholder: b.props.placeholder, isMovingWithParent: b.props.isMovingWithParent })), isInSlideshow: targetSlideshow ? this.isSlideInSlideshow(shape, targetSlideshow) : false }); if (targetSlideshow && this.isSlideInSlideshow(shape, targetSlideshow)) { const index = this.getBindingIndexForPosition(shape, targetSlideshow, pageAnchor); const existingBinding = bindings[0]; if (existingBinding && existingBinding.fromId === targetSlideshow.id) { logger.debug("shape", "βœ… onTranslateEnd: Updating existing binding", { slideId: shape.id, slideshowId: targetSlideshow.id, bindingId: existingBinding.id, index }); this.editor.updateBinding({ id: existingBinding.id, type: existingBinding.type, fromId: existingBinding.fromId, toId: existingBinding.toId, props: { index, isMovingWithParent: true, placeholder: false } }); } else { logger.debug("shape", "βœ… onTranslateEnd: Creating new binding", { slideId: shape.id, slideshowId: targetSlideshow.id, index }); this.editor.deleteBindings(bindings); this.editor.createBinding({ type: "cc-slide-layout", fromId: targetSlideshow.id, toId: shape.id, props: { index, isMovingWithParent: true, placeholder: false } }); } } else { logger.debug("shape", "βœ… onTranslateEnd: Removing bindings", { slideId: shape.id, bindings: bindings.map((b) => b.id) }); this.editor.deleteBindings(bindings); } }; renderContent = (shape) => { return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { width: "100%", height: "100%", position: "relative", backgroundColor: "white", borderRadius: "4px", overflow: "hidden" }, children: shape.props.imageData && /* @__PURE__ */ jsxRuntimeExports.jsx( "img", { src: shape.props.imageData, alt: shape.props.title, style: { width: "100%", height: `100%`, objectFit: "contain", position: "absolute", top: 0, left: 0 } } ) }); }; } var n,l$1,u$1,i$3,t,r$1,o,f$1,e$1,c$2={},s=[],a$1=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function h(n,l){for(var u in l)n[u]=l[u];return n}function v$1(n){var l=n.parentNode;l&&l.removeChild(n);}function y(l,u,i){var t,r,o,f={};for(o in u)"key"==o?t=u[o]:"ref"==o?r=u[o]:f[o]=u[o];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),"function"==typeof l&&null!=l.defaultProps)for(o in l.defaultProps) void 0===f[o]&&(f[o]=l.defaultProps[o]);return p(l,f,t,r,null)}function p(n,i,t,r,o){var f={type:n,props:i,key:t,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==o?++u$1:o};return null==o&&null!=l$1.vnode&&l$1.vnode(f),f}function d(){return {current:null}}function _(n){return n.children}function k$1(n,l,u,i,t){var r;for(r in u)"children"===r||"key"===r||r in l||g$2(n,r,null,u[r],i);for(r in l)t&&"function"!=typeof l[r]||"children"===r||"key"===r||"value"===r||"checked"===r||u[r]===l[r]||g$2(n,r,l[r],u[r],i);}function b$1(n,l,u){"-"===l[0]?n.setProperty(l,null==u?"":u):n[l]=null==u?"":"number"!=typeof u||a$1.test(l)?u:u+"px";}function g$2(n,l,u,i,t){var r;n:if("style"===l)if("string"==typeof u)n.style.cssText=u;else {if("string"==typeof i&&(n.style.cssText=i=""),i)for(l in i)u&&l in u||b$1(n.style,l,"");if(u)for(l in u)i&&u[l]===i[l]||b$1(n.style,l,u[l]);}else if("o"===l[0]&&"n"===l[1])r=l!==(l=l.replace(/Capture$/,"")),l=l.toLowerCase()in n?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+r]=u,u?i||n.addEventListener(l,r?w$2:m$1,r):n.removeEventListener(l,r?w$2:m$1,r);else if("dangerouslySetInnerHTML"!==l){if(t)l=l.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!==l&&"height"!==l&&"href"!==l&&"list"!==l&&"form"!==l&&"tabIndex"!==l&&"download"!==l&&l in n)try{n[l]=null==u?"":u;break n}catch(n){}"function"==typeof u||(null==u||false===u&&-1==l.indexOf("-")?n.removeAttribute(l):n.setAttribute(l,u));}}function m$1(n){t=true;try{return this.l[n.type+!1](l$1.event?l$1.event(n):n)}finally{t=false;}}function w$2(n){t=true;try{return this.l[n.type+!0](l$1.event?l$1.event(n):n)}finally{t=false;}}function x$1(n,l){this.props=n,this.context=l;}function A(n,l){if(null==l)return n.__?A(n.__,n.__.__k.indexOf(n)+1):null;for(var u;ll&&r$1.sort(function(n,l){return n.__v.__b-l.__v.__b}));$$1.__r=0;}function H$1(n,l,u,i,t,r,o,f,e,a){var h,v,y,d,k,b,g,m=i&&i.__k||s,w=m.length;for(u.__k=[],h=0;h0?p(d.type,d.props,d.key,d.ref?d.ref:null,d.__v):d)){if(d.__=u,d.__b=u.__b+1,null===(y=m[h])||y&&d.key==y.key&&d.type===y.type)m[h]=void 0;else for(v=0;v=0;l--)if((u=n.__k[l])&&(i=L$1(u)))return i;return null}function M(n,u,i,t,r,o,f,e,c){var s,a,v,y,p,d,k,b,g,m,w,A,P,C,T,$=u.type;if(void 0!==u.constructor)return null;null!=i.__h&&(c=i.__h,e=u.__e=i.__e,u.__h=null,o=[e]),(s=l$1.__b)&&s(u);try{n:if("function"==typeof $){if(b=u.props,g=(s=$.contextType)&&t[s.__c],m=s?g?g.props.value:s.__:t,i.__c?k=(a=u.__c=i.__c).__=a.__E:("prototype"in $&&$.prototype.render?u.__c=a=new $(b,m):(u.__c=a=new x$1(b,m),a.constructor=$,a.render=B$1),g&&g.sub(a),a.props=b,a.state||(a.state={}),a.context=m,a.__n=t,v=a.__d=!0,a.__h=[],a._sb=[]),null==a.__s&&(a.__s=a.state),null!=$.getDerivedStateFromProps&&(a.__s==a.state&&(a.__s=h({},a.__s)),h(a.__s,$.getDerivedStateFromProps(b,a.__s))),y=a.props,p=a.state,a.__v=u,v)null==$.getDerivedStateFromProps&&null!=a.componentWillMount&&a.componentWillMount(),null!=a.componentDidMount&&a.__h.push(a.componentDidMount);else {if(null==$.getDerivedStateFromProps&&b!==y&&null!=a.componentWillReceiveProps&&a.componentWillReceiveProps(b,m),!a.__e&&null!=a.shouldComponentUpdate&&!1===a.shouldComponentUpdate(b,a.__s,m)||u.__v===i.__v){for(u.__v!==i.__v&&(a.props=b,a.state=a.__s,a.__d=!1),u.__e=i.__e,u.__k=i.__k,u.__k.forEach(function(n){n&&(n.__=u);}),w=0;w3;)e.pop()();if(e[1]>>1,1),e.i.removeChild(n);}}),D$1(y(P,{context:e.context},n.__v),e.l)):e.l&&e.componentWillUnmount();}function j(n,e){var r=y($,{__v:n,i:e});return r.containerInfo=e,r}(V.prototype=new x$1).__a=function(n){var t=this,e=F(t.__v),r=t.o.get(n);return r[0]++,function(u){var o=function(){t.props.revealOrder?(r.push(u),W(t,n,r)):u();};e?e(o):o();}},V.prototype.render=function(n){this.u=null,this.o=new Map;var t=j$2(n.children);n.revealOrder&&"b"===n.revealOrder[0]&&t.reverse();for(var e=t.length;e--;)this.o.set(t[e],this.u=[1,0,this.u]);return n.children},V.prototype.componentDidUpdate=V.prototype.componentDidMount=function(){var n=this;this.o.forEach(function(t,e){W(n,e,t);});};var z="undefined"!=typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103,B=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,H="undefined"!=typeof document,Z=function(n){return ("undefined"!=typeof Symbol&&"symbol"==typeof Symbol()?/fil|che|rad/i:/fil|che|ra/i).test(n)};x$1.prototype.isReactComponent={},["componentWillMount","componentWillReceiveProps","componentWillUpdate"].forEach(function(t){Object.defineProperty(x$1.prototype,t,{configurable:true,get:function(){return this["UNSAFE_"+t]},set:function(n){Object.defineProperty(this,t,{configurable:true,writable:true,value:n});}});});var G=l$1.event;function J(){}function K(){return this.cancelBubble}function Q(){return this.defaultPrevented}l$1.event=function(n){return G&&(n=G(n)),n.persist=J,n.isPropagationStopped=K,n.isDefaultPrevented=Q,n.nativeEvent=n};var nn={configurable:true,get:function(){return this.class}},tn=l$1.vnode;l$1.vnode=function(n){var t=n.type,e=n.props,u=e;if("string"==typeof t){var o=-1===t.indexOf("-");for(var i in u={},e){var l=e[i];H&&"children"===i&&"noscript"===t||"value"===i&&"defaultValue"in e&&null==l||("defaultValue"===i&&"value"in e&&null==e.value?i="value":"download"===i&&true===l?l="":/ondoubleclick/i.test(i)?i="ondblclick":/^onchange(textarea|input)/i.test(i+t)&&!Z(e.type)?i="oninput":/^onfocus$/i.test(i)?i="onfocusin":/^onblur$/i.test(i)?i="onfocusout":/^on(Ani|Tra|Tou|BeforeInp|Compo)/.test(i)?i=i.toLowerCase():o&&B.test(i)?i=i.replace(/[A-Z0-9]/g,"-$&").toLowerCase():null===l&&(l=void 0),/^oninput$/i.test(i)&&(i=i.toLowerCase(),u[i]&&(i="oninputCapture")),u[i]=l);}"select"==t&&u.multiple&&Array.isArray(u.value)&&(u.value=j$2(e.children).forEach(function(n){n.props.selected=-1!=u.value.indexOf(n.props.value);})),"select"==t&&null!=u.defaultValue&&(u.value=j$2(e.children).forEach(function(n){n.props.selected=u.multiple?-1!=u.defaultValue.indexOf(n.props.value):u.defaultValue==n.props.value;})),n.props=u,e.class!=e.className&&(nn.enumerable="className"in e,null!=e.className&&(u.class=e.className),Object.defineProperty(u,"className",nn));}n.$$typeof=z,tn&&tn(n);};var en=l$1.__r;l$1.__r=function(n){en&&en(n),n.__c;}; const styleTexts = []; const styleEls = new Map(); function injectStyles(styleText) { styleTexts.push(styleText); styleEls.forEach((styleEl) => { appendStylesTo(styleEl, styleText); }); } function ensureElHasStyles(el) { if (el.isConnected && // sometimes true if SSR system simulates DOM el.getRootNode // sometimes undefined if SSR system simulates DOM ) { registerStylesRoot(el.getRootNode()); } } function registerStylesRoot(rootNode) { let styleEl = styleEls.get(rootNode); if (!styleEl || !styleEl.isConnected) { styleEl = rootNode.querySelector('style[data-fullcalendar]'); if (!styleEl) { styleEl = document.createElement('style'); styleEl.setAttribute('data-fullcalendar', ''); const nonce = getNonceValue(); if (nonce) { styleEl.nonce = nonce; } const parentEl = rootNode === document ? document.head : rootNode; const insertBefore = rootNode === document ? parentEl.querySelector('script,link[rel=stylesheet],link[as=style],style') : parentEl.firstChild; parentEl.insertBefore(styleEl, insertBefore); } styleEls.set(rootNode, styleEl); hydrateStylesRoot(styleEl); } } function hydrateStylesRoot(styleEl) { for (const styleText of styleTexts) { appendStylesTo(styleEl, styleText); } } function appendStylesTo(styleEl, styleText) { const { sheet } = styleEl; const ruleCnt = sheet.cssRules.length; styleText.split('}').forEach((styleStr, i) => { styleStr = styleStr.trim(); if (styleStr) { sheet.insertRule(styleStr + '}', ruleCnt + i); } }); } // nonce // ------------------------------------------------------------------------------------------------- let queriedNonceValue; function getNonceValue() { if (queriedNonceValue === undefined) { queriedNonceValue = queryNonceValue(); } return queriedNonceValue; } /* TODO: discourage meta tag and instead put nonce attribute on placeholder