UNPKG

web-mojo

Version:

WEB-MOJO - A lightweight JavaScript framework for building data-driven web applications

1 lines 55.6 kB
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./chunks/WebApp-sKJf8j1s.js"),t=require("./chunks/Page-D5kSAjt8.js"),s=require("./chunks/User-CvWFN0ul.js"),i=require("./chunks/FilePreviewView-CxplM_0z.js"),a=require("./chunks/TopNav-DlHcfQbu.js"),n=require("./chunks/TokenManager-gp1JKXNd.js"),o=require("./chunks/Dialog-BmvpkgLD.js"),r=require("./chunks/DataView-BwZajXhu.js"),l=require("./chunks/FormView-BDBRWOlR.js"),c=require("./chunks/WebSocketClient-D6i85jl2.js");class ResultsView extends e.View{constructor(e={}){super({className:"search-results-view flex-grow-1 overflow-auto d-flex flex-column",template:'\n <div class="flex-grow-1 overflow-auto">\n {{#data.loading}}\n <div class="text-center p-4">\n <div class="spinner-border spinner-border-sm text-muted" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <div class="mt-2 small text-muted">{{data.loadingText}}</div>\n </div>\n {{/data.loading}}\n\n {{^data.loading}}\n {{#data.items}}\n <div class="simple-search-item position-relative"\n data-action="select-item"\n data-item-index="{{index}}">\n {{{itemContent}}}\n <i class="bi bi-chevron-right position-absolute end-0 top-50 translate-middle-y me-3 text-muted"></i>\n </div>\n {{/data.items}}\n\n {{#data.showNoResults}}\n <div class="text-center p-4">\n <i class="bi bi-search text-muted mb-2" style="font-size: 1.5rem;"></i>\n <div class="text-muted small">{{data.noResultsText}}</div>\n <button type="button"\n class="btn btn-link btn-sm mt-2 p-0"\n data-action="clear-search">\n Clear search\n </button>\n </div>\n {{/data.showNoResults}}\n\n {{#data.showEmpty}}\n <div class="text-center p-4">\n <i class="{{data.emptyIcon}} text-muted mb-2" style="font-size: 2rem;"></i>\n <div class="text-muted small mb-2">{{data.emptyText}}</div>\n {{#data.emptySubtext}}\n <div class="text-muted" style="font-size: 0.75rem;">\n {{data.emptySubtext}}\n </div>\n {{/data.emptySubtext}}\n </div>\n {{/data.showEmpty}}\n {{/data.loading}}\n </div>\n\n {{#data.showResultsCount}}\n <div class="border-top bg-light p-2 text-center">\n <small class="text-muted">\n {{data.filteredCount}} of {{data.totalCount}}\n </small>\n </div>\n {{/data.showResultsCount}}\n ',...e}),this.parentView=e.parentView}async handleActionSelectItem(e,t){e.preventDefault();const s=parseInt(t.getAttribute("data-item-index"));this.parentView&&this.parentView.handleItemSelection(s)}async handleActionClearSearch(e,t){e.preventDefault(),this.parentView&&this.parentView.clearSearch()}}class SimpleSearchView extends e.View{constructor(e={}){super({className:"simple-search-view h-100 d-flex flex-column",template:'\n <div class="p-3 border-bottom bg-light">\n <div class="d-flex justify-content-between align-items-start mb-3">\n <h6 class="text-muted fw-semibold mb-0">\n {{#data.headerIcon}}<i class="{{data.headerIcon}} me-2"></i>{{/data.headerIcon}}\n {{{data.headerText}}}\n </h6>\n {{#data.showExitButton}}\n <button class="btn btn-link p-0 text-muted simple-search-exit-btn"\n type="button"\n data-action="exit-view"\n title="Exit"\n aria-label="Exit view">\n <i class="bi bi-x-lg" aria-hidden="true"></i>\n </button>\n {{/data.showExitButton}}\n </div>\n <div class="position-relative">\n <input type="text"\n class="form-control form-control-sm pe-5"\n placeholder="{{data.searchPlaceholder}}"\n value="{{data.searchValue}}"\n data-filter="live-search"\n data-filter-debounce="{{data.debounceMs}}"\n data-change-action="search-items">\n <button class="btn btn-link p-0 position-absolute top-50 end-0 translate-middle-y me-2 text-muted simple-search-clear-btn"\n type="button"\n data-action="clear-search"\n title="Clear search"\n aria-label="Clear search">\n <i class="bi bi-x-circle-fill" aria-hidden="true"></i>\n </button>\n </div>\n </div>\n\n <div data-container="results"></div>\n\n {{#data.showFooter}}\n <div class="p-3 border-top bg-light">\n <small class="text-muted">\n <i class="{{data.footerIcon}} me-1"></i>\n {{{data.footerContent}}}\n </small>\n </div>\n {{/data.showFooter}}\n ',...e}),this.Collection=e.Collection,this.collection=e.collection,this.itemTemplate=e.itemTemplate||this.getDefaultItemTemplate(),this.searchFields=e.searchFields||["name"],this.collectionParams={size:25,...e.collectionParams},this.headerText=e.headerText||"Select Item",this.headerIcon=e.headerIcon||"bi bi-list",this.searchPlaceholder=e.searchPlaceholder||"Search...",this.loadingText=e.loadingText||"Loading items...",this.noResultsText=e.noResultsText||"No items match your search",this.emptyText=e.emptyText||"No items available",this.emptySubtext=e.emptySubtext||null,this.emptyIcon=e.emptyIcon||"bi bi-inbox",this.footerContent=e.footerContent||null,this.footerIcon=e.footerIcon||"bi bi-info-circle",this.showExitButton=e.showExitButton||!1,this.searchValue="",this.filteredItems=[],this.loading=!1,this.hasSearched=!1,this.searchTimer=null,this.debounceMs=e.debounceMs||800,this.resultsView=new ResultsView({parentView:this}),!this.collection&&this.Collection&&(this.collection=new this.Collection),this.addChild(this.resultsView)}onInit(){this.collection&&this.setupCollection(),this.collection&&!1!==this.options.autoLoad&&this.loadItems()}setupCollection(){Object.assign(this.collection.params,this.collectionParams),this.collection.on("fetch:success",()=>{this.loading=!1,this.updateFilteredItems()}),this.collection.on("fetch:error",()=>{this.loading=!1})}async loadItems(){if(this.collection){this.loading=!0,this.updateResultsView();try{await this.collection.fetch(),this.updateFilteredItems()}catch(e){console.error("Error loading items:",e);const t=this.getApp();t?.showError?.("Failed to load items. Please try again.")}finally{this.loading=!1,this.updateFilteredItems()}}else console.warn("SimpleSearchView: No collection provided")}updateFilteredItems(){if(!this.collection)return void(this.filteredItems=[]);const e=this.collection.toJSON();if(this.searchValue&&this.searchValue.trim()){const t=this.searchValue.toLowerCase().trim();this.filteredItems=e.filter(e=>this.searchFields.some(s=>{const i=this.getNestedValue(e,s);return i&&i.toString().toLowerCase().includes(t)}))}else this.filteredItems=e;this.updateResultsView()}getNestedValue(e,t){return t.split(".").reduce((e,t)=>e?.[t],e)}async getViewData(){return{searchValue:this.searchValue,showFooter:!!this.footerContent,showExitButton:this.showExitButton,debounceMs:this.debounceMs,headerText:this.headerText,headerIcon:this.headerIcon,searchPlaceholder:this.searchPlaceholder,footerContent:this.footerContent,footerIcon:this.footerIcon}}updateResultsView(){if(!this.resultsView)return;const e=this.collection&&this.collection.length()>0,t=this.filteredItems.length>0,s=this.searchValue.length>0,i=this.filteredItems.map((e,t)=>({...e,index:t,itemContent:this.processItemTemplate(e)}));this.resultsView.data={loading:this.loading,items:i,showEmpty:!this.loading&&!e,showNoResults:!this.loading&&e&&!t&&s,showResultsCount:!this.loading&&e,filteredCount:this.filteredItems.length,totalCount:this.collection?.restEnabled?this.collection?.meta?.count||0:this.collection?.length()||0,loadingText:this.loadingText,noResultsText:this.noResultsText,emptyText:this.emptyText,emptySubtext:this.emptySubtext,emptyIcon:this.emptyIcon},this.resultsView.render()}processItemTemplate(e){let t=this.itemTemplate;return t=t.replace(/\{\{(\w+)\}\}/g,(t,s)=>this.getNestedValue(e,s)||""),t}getDefaultItemTemplate(){return'\n <div class="p-3 border-bottom">\n <div class="fw-semibold text-dark">{{name}}</div>\n <small class="text-muted">{{id}}</small>\n </div>\n '}async onPassThruActionSearchItems(e,t){const s=t.value||"";console.log("search change..."),this.searchValue=s,this.hasSearched=!0,this.searchTimer&&clearTimeout(this.searchTimer),this.performSearch()}async performSearch(){const e={...this.collectionParams};this.searchValue&&this.searchValue.length>1&&(e.search=this.searchValue.trim()),this.collection.setParams(e,!0)}handleItemSelection(e){if(isNaN(e)||e<0||e>=this.filteredItems.length)return void console.error("Invalid item index:",e);const t=this.filteredItems[e],s=this.collection?this.collection.get(t.id):null;this.emit("item:selected",{item:t,model:s,index:e})}setCollection(e){return this.collection=e,this.setupCollection(),this}setItemTemplate(e){return this.itemTemplate=e,this.updateResultsView(),this}setSearchFields(e){return this.searchFields=Array.isArray(e)?e:[e],this}async refresh(){await this.loadItems()}focusSearch(){const e=this.element?.querySelector('input[data-action="search-items"]');e&&e.focus()}async handleActionExitView(e,t){this.emit("exit",{view:this})}async handleActionClearSearch(e,t){this.clearSearch()}clearSearch(){this.searchValue="",this.hasSearched=!1;const e=this.element?.querySelector('input[data-change-action="search-items"]');e&&(e.value="",e.focus()),this.performSearch()}getItemCount(){return this.collection?this.collection.length():0}getFilteredItemCount(){return this.filteredItems.length}hasItems(){return this.getItemCount()>0}getSearchValue(){return this.searchValue}setSearchValue(e){this.searchValue=e||"",this.hasSearched=!!this.searchValue;const t=this.element?.querySelector('input[data-action="search-items"]');return t&&(t.value=this.searchValue),this.performSearch(),this}async onAfterRender(){if(await super.onAfterRender(),this.resultsView&&!this.resultsView.isMounted()){const e=this.element?.querySelector('[data-container="results"]');e&&await this.resultsView.render(!0,e)}this.updateResultsView()}async onBeforeDestroy(){this.searchTimer&&clearTimeout(this.searchTimer),this.collection&&this.collection.off("update"),await super.onBeforeDestroy()}}class Sidebar extends e.View{constructor(e={}){super({tagName:"nav",className:"sidebar",id:"sidebar",...e}),this.menus=/* @__PURE__ */new Map,this.activeMenuName=null,this.currentRoute=null,this.showToggle=e.showToggle,this.isCollapsed=!1,this.sidebarTheme=e.theme||"sidebar-light",this.customView=null,this.sidebarTheme&&this.addClass(this.sidebarTheme),this.initializeMenus(e),this.setupRouteListeners(),!1!==e.autoCollapseMobile&&this.setupResponsiveBehavior()}async onInit(){await super.onInit();const e=this.getApp(),t=e?.router;if(t){const e=t.getCurrentPath();e&&this.autoSwitchToMenuForRoute(e)}this.initializeTooltips(),this.searchView=new SimpleSearchView({noAppend:!0,showExitButton:!0,headerText:"Select Group",containerId:"sidebar-search-container",Collection:s.GroupList,itemTemplate:'\n <div class="p-3 border-bottom">\n <div class="fw-semibold text-dark">{{name}}</div>\n <small class="text-muted">#{{id}} {{kind}}</small>\n </div>\n '}),this.addChild(this.searchView),this.searchView.on("item:selected",e=>{console.log(e),this.getApp().setActiveGroup(e.model)}),this.searchView.on("exit",e=>{console.log(e),this.hideGroupSearch()})}showGroupSearch(){this.setClass("sidebar"),this.showSearch=!0,this.render()}hideGroupSearch(){this.setClass("sidebar"),this.showSearch=!1,this.render()}onActionShowGroupSearch(){this.showGroupSearch()}autoSwitchToMenuForRoute(e){for(const[t,s]of this.menus)if((!s.groupKind||this.getApp().activeGroup)&&this.menuContainsRoute(s,e))return this._setActiveMenu(t),this.currentRoute=e,this.clearAllActiveStates(),this.setActiveItemByRoute(e),this.render(),console.log(`Auto-switched to menu '${t}' for route '${e}'`),this.emit("menu-auto-switched",{menuName:t,route:e,config:s,sidebar:this}),!0;return!1}clearAllActiveStates(){for(const[e,t]of this.menus)for(const s of t.items||[])if(s.active=!1,s.children)for(const e of s.children)e.active=!1}setActiveItemByRoute(e){const t=e=>{if(!e)return"/";const t=decodeURIComponent(e);return t.startsWith("/")?t:`/${t}`},s=t(e);for(const[i,a]of this.menus)if(!a.groupKind||this.getApp().activeGroup)for(const e of a.items||[]){if(e.route){const i=t(e.route);if(this.routesMatch(s,i))return e.active=!0,this.activeMenuItem=e,!0}if(e.children)for(const i of e.children)if(i.route){const a=t(i.route);if(this.routesMatch(s,a))return i.active=!0,e.active=!0,!0}}return!1}menuContainsRoute(e,t){const s=e=>{if(!e)return"/";const t=decodeURIComponent(e);return t.startsWith("/")?t:`/${t}`},i=s(t);for(const a of e.items||[]){if(a.route){const e=s(a.route);if(this.routesMatch(i,e))return!0}if(a.children)for(const e of a.children)if(e.route){const t=s(e.route);if(this.routesMatch(i,t))return!0}}return!1}routesMatch(e,t){return this.getApp().router.doRoutesMatch(e,t)}getTemplate(){return this.customView?'<div class="sidebar-container" id="sidebar-custom-view-container"></div>':this.showSearch?this.getSearchTemplate():this.getMenuTemplate()}getSearchTemplate(){return'\n <div class="sidebar-container" id="sidebar-search-container">\n </div>\n '}getMenuTemplate(){return'\n <div class="sidebar-container">\n {{#data.currentMenu}}\n \x3c!-- Header --\x3e\n {{#header}}\n <div class="sidebar-header">\n {{{header}}}\n {{#showToggle}}\n <button class="sidebar-toggle" data-action="toggle-sidebar"\n aria-label="Toggle Sidebar">\n <i class="bi bi-chevron-left toggle-icon"></i>\n <i class="bi bi-chevron-right toggle-icon"></i>\n </button>\n {{/showToggle}}\n </div>\n {{/header}}\n\n \x3c!-- Navigation Items --\x3e\n <div class="sidebar-body">\n <ul class="nav nav-pills flex-column sidebar-nav" id="sidebar-nav-menu">\n {{#items}}\n {{>nav-item}}\n {{/items}}\n </ul>\n </div>\n\n \x3c!-- Footer --\x3e\n {{#footer}}\n <div class="sidebar-footer">\n {{{footer}}}\n </div>\n {{/footer}}\n {{/data.currentMenu}}\n\n {{^data.currentMenu}}\n <div class="sidebar-empty">\n <p class="text-danger text-center">No menu configured</p>\n </div>\n {{/data.currentMenu}}\n </div>\n '}getPartials(){return{"nav-item":'\n {{#isDivider}}\n {{>nav-divider}}\n {{/isDivider}}\n {{#isSpacer}}\n {{>nav-spacer}}\n {{/isSpacer}}\n {{#isLabel}}\n {{>nav-label}}\n {{/isLabel}}\n\n {{^isDivider}}\n {{^isSpacer}}\n {{^isLabel}}\n <li class="nav-item">\n {{#hasChildren}}\n \x3c!-- Item with submenu --\x3e\n <a class="nav-link {{#active}}active{{/active}} has-children collapsed"\n data-bs-toggle="collapse"\n href="#collapse-{{id}}"\n role="button"\n aria-expanded="{{#active}}true{{/active}}{{^active}}false{{/active}}"\n data-action="toggle-submenu">\n {{#icon}}<i class="{{icon}} me-2"></i>{{/icon}}\n <span class="nav-text">{{text}}</span>\n {{#badge}}\n <span class="{{badge.class}} ms-auto">{{badge.text}}</span>\n {{/badge}}\n <i class="bi bi-chevron-down nav-arrow ms-auto"></i>\n </a>\n <div class="collapse {{#active}}show{{/active}}" id="collapse-{{id}}" data-bs-parent="#sidebar-nav-menu">\n <ul class="nav flex-column nav-submenu">\n {{#children}}\n <li class="nav-item">\n <a class="nav-link {{#active}}active{{/active}}"\n {{#action}}data-action="{{action}}"{{/action}}\n {{#href}}href="{{href}}"{{/href}}>\n {{#icon}}<i class="{{icon}} me-2"></i>{{/icon}}\n <span class="nav-text">{{text}}</span>\n {{#badge}}\n <span class="{{badge.class}} ms-auto">{{badge.text}}</span>\n {{/badge}}\n </a>\n </li>\n {{/children}}\n </ul>\n </div>\n {{/hasChildren}}\n {{^hasChildren}}\n \x3c!-- Simple item --\x3e\n <a class="nav-link {{#active}}active{{/active}} {{#disabled}}disabled{{/disabled}}"\n {{#action}}{{^disabled}}data-action="{{action}}"{{/disabled}}{{/action}}\n {{#href}}{{^disabled}}href="{{href}}"{{/disabled}}{{/href}}>\n {{#icon}}<i class="{{icon}} me-2"></i>{{/icon}}\n <span class="nav-text">{{text}}</span>\n {{#badge}}\n <span class="{{badge.class}} ms-auto">{{badge.text}}</span>\n {{/badge}}\n </a>\n {{/hasChildren}}\n </li>\n {{/isLabel}}\n {{/isSpacer}}\n {{/isDivider}}\n ',"nav-divider":'\n <li class="nav-divider-item">\n <hr class="nav-divider-line">\n </li>\n ',"nav-spacer":'\n <li class="nav-spacer-item"></li>\n ',"nav-label":'\n <li class="nav-item {{className}}">\n <div class="nav-text px-3">{{text}}</div>\n </li>\n '}}getGroupHeader(){return"\n <div class=\"sidebar-group-header py-3\" data-action=\"show-group-search\">\n <div class='text-center text-muted fs-7 collapsed-hidden'>active group</div>\n <div class='text-center fs-5 px-1 collapsed-hidden'>{{group.name}}</div>\n <div class='text-center fs-6 collapsed-hidden'>kind: {{group.kind}}</div>\n </div>\n "}addMenu(e,t){return t.groupKind&&!t.header&&(t.header=this.getGroupHeader()),this.menus.set(e,{name:e,groupKind:t.groupKind||null,header:t.header||null,footer:t.footer||null,items:t.items||[],data:t.data||{},className:t.className||"sidebar sidebar-dark"}),this.activeMenuName||this._setActiveMenu(e),this}_setActiveMenu(e){this.showSearch=!1,this.activeMenuName=e;const t=this.getCurrentMenuConfig();t.className?this.setClass(t.className):this.setClass("sidebar")}async setActiveMenu(e){if(!this.menus.has(e))return console.warn(`Menu '${e}' not found`),this;const t=this.menus.get(e);if(!t.groupKind||(this.lastGroupMenu=t,this.getApp().activeGroup))return this._setActiveMenu(e),await this.render(),this.emit("menu-changed",{menuName:e,config:t,sidebar:this}),this;this.showGroupSearch()}getGroupMenu(e){if(!e)return console.warn("No group provided"),null;let t=this.lastGroupMenu,s=null;if(e._.kind)for(const[i,a]of this.menus){if(a.groupKind===e._.kind){t=a;break}"any"===a.groupKind&&(s=a)}return t||s}showMenuForGroup(e){if(!e)return void console.warn("No group provided");let t=this.getGroupMenu(e);if(t)return this._setActiveMenu(t.name),this.render(),this.emit("menu-changed",{menuName:t.name,config:t,sidebar:this}),this;console.warn(`No menu found for group kind: ${e.kind}`)}getMenuConfig(e){return this.menus.get(e)||null}getCurrentMenuConfig(){return this.activeMenuName?this.menus.get(this.activeMenuName):null}updateMenu(e,t){const s=this.menus.get(e);return s?(Object.assign(s,t),this.activeMenuName===e&&this.render(),this):(console.warn(`Menu '${e}' not found`),this)}removeMenu(e){if(this.menus.delete(e),this.activeMenuName===e){const e=Array.from(this.menus.keys());this.activeMenuName=e.length>0?e[0]:null,this.render()}return this}async onBeforeRender(){const e=this.getCurrentMenuConfig();if(!e)return{currentMenu:null};let t={version:this.getApp().version||null,group:this.getApp().activeGroup||null,user:this.getApp.activeUser||null};this.data={currentMenu:{header:this.renderTemplateString(e.header||"",t),footer:this.renderTemplateString(e.footer||"",t),items:this.processNavItems(e.items,e.groupKind),data:e.data,showToggle:this.showToggle}}}async onAfterRender(){this.isCollapsedState()?setTimeout(()=>this.initializeTooltips(),50):this.destroyTooltips()}setCustomView(e){return this.customView&&this.removeChild(this.customView.id),this.customView=e,e&&(e.containerId="sidebar-custom-view-container",this.addChild(e)),this.render(),this}clearCustomView(){return this.customView&&(this.removeChild(this.customView.id),this.customView=null),this.render(),this}processNavItems(e,t){const s=this.getApp(),i=s?.activeUser,a=s?.activeGroup,n=e=>{if(t&&a&&a.id){const t=e.includes("?")?"&":"?";return`${e}${t}group=${a.id}`}return e};return e.map((e,t)=>{if(""===e||"object"==typeof e&&e.divider)return{isDivider:!0,id:`divider-${t}`};if("object"==typeof e&&e.spacer)return{isSpacer:!0,id:`spacer-${t}`};const s={...e};if(s.permissions&&(!i||!i.hasPermission(s.permissions)))return null;if("label"===s.kind)return s.isLabel=!0,s.id||(s.id=`nav-label-${t}`),s;if(s.id||(s.id=`nav-${t}`),s.route)s.href=n(s.route);else if(s.page){const e=s.page.startsWith("/")?s.page:`/${s.page}`;s.href=n(e),s.route=s.href}return s.children?(s.children=s.children.map(e=>{const t={...e};if(t.permissions&&i&&!i.hasPermission(t.permissions))return null;if(t.route)t.href=n(t.route);else if(t.page){const e=t.page.startsWith("/")?t.page:`/${t.page}`;t.href=n(e),t.route=t.href}return t}).filter(e=>null!==e),s.hasChildren=!!(s.children&&s.children.length>0)):s.hasChildren=!1,s}).filter(e=>null!==e)}isItemActive(e){if(!e.route||!this.currentRoute)return!1;const t=e=>{if(!e)return"/";const t=decodeURIComponent(e);return t.startsWith("/")?t:`/${t}`},s=t(e.route),i=t(this.currentRoute);return"/"===s&&"/"===i||"/"!==s&&"/"!==i&&(i.startsWith(s)||i===s)}async updateActiveItem(e){return this.currentRoute=e,this.clearAllActiveStates(),this.setActiveItemByRoute(e),await this.render(),this}async handleActionToggleSubmenu(e,t){const s=t.querySelector(".nav-arrow");s&&s.classList.toggle("rotated")}async handleActionToggleSidebar(e,t){this.toggleSidebar()}onActionShowGroupMenu(e,t,s){return this.setActiveMenu("group_default"),!1}async onActionDefault(e,t,s){const i=this.getCurrentMenuConfig();if(i){for(const a of i.items)if(a.action==e&&a.handler)return a.handler(e,t,s),!0;return!1}}getMenuNames(){return Array.from(this.menus.keys())}hasMenu(e){return this.menus.has(e)}clearMenus(){return this.menus.clear(),this.activeMenuName=null,this.render(),this}setMenuData(e){const t=this.getCurrentMenuConfig();return t&&(t.data={...t.data,...e},this.render()),this}getMenuData(){const e=this.getCurrentMenuConfig();return e?e.data:{}}setupRouteListeners(){const e=this.getApp();e&&e.events&&(e.events.on(["page:show","page:hide","page:denied"],e=>{this.onRouteChanged(e)}),e.events.on("group:changed",e=>{this.showMenuForGroup(e.group)}),e.events.on("portal:user-changed",e=>{this.render()}))}onRouteChanged(e){if(e.page&&e.page.route){const t=e.page.route;if(this.activeMenuItem&&this.routesMatch(t,this.activeMenuItem.route))return;const s=this.autoSwitchToMenuForRoute(t);s||(this.clearAllActiveStates(),this.setActiveItemByRoute(t),this.updateActiveItem(t)),s&&console.log(`Route changed to '${t}', auto-switched menu`)}}toggleSidebar(){const e=document.querySelector(".portal-container");if(!e)return;this.hideAllTooltips();const t=e.classList.contains("collapse-sidebar");return e.classList.contains("hide-sidebar")?(e.classList.remove("hide-sidebar"),this.isCollapsed=!1,this.destroyTooltips()):t?(e.classList.remove("collapse-sidebar"),this.isCollapsed=!1,this.destroyTooltips()):(e.classList.add("collapse-sidebar"),this.isCollapsed=!0,setTimeout(()=>this.initializeTooltips(),150)),this}setSidebarState(e){const t=document.querySelector(".portal-container");if(!t)return this;switch(t.classList.remove("collapse-sidebar","hide-sidebar"),e){case"collapsed":t.classList.add("collapse-sidebar"),this.isCollapsed=!0;break;case"hidden":t.classList.add("hide-sidebar"),this.isCollapsed=!1;break;default:this.isCollapsed=!1}return this.isCollapsed?(this.hideAllTooltips(),setTimeout(()=>this.initializeTooltips(),100)):this.destroyTooltips(),this}initializeTooltips(){return this.destroyTooltips(),this.isCollapsedState()?(this.element.querySelectorAll(".sidebar-nav .nav-link").forEach(e=>{const t=e.querySelector(".nav-text");if(t&&t.textContent.trim()){const s=t.textContent.trim();if(e.setAttribute("data-bs-toggle","tooltip"),e.setAttribute("data-bs-placement","right"),e.setAttribute("data-bs-title",s),e.setAttribute("data-bs-container","body"),window.bootstrap&&window.bootstrap.Tooltip){const t=new window.bootstrap.Tooltip(e,{placement:"right",container:"body",trigger:"hover",delay:{show:500,hide:100},fallbackPlacements:["top","bottom","left"]});e._tooltipInstance=t,e.addEventListener("click",()=>{t.hide()}),e.addEventListener("blur",()=>{t.hide()})}}}),this.addTooltipHideListeners(),this):this}destroyTooltips(){return this.removeTooltipHideListeners(),this.element.querySelectorAll('.sidebar-nav .nav-link[data-bs-toggle="tooltip"]').forEach(e=>{const t=e._tooltipInstance||window.bootstrap?.Tooltip?.getInstance(e);t&&(t.hide(),t.dispose()),delete e._tooltipInstance,e.removeAttribute("data-bs-toggle"),e.removeAttribute("data-bs-placement"),e.removeAttribute("data-bs-title"),e.removeAttribute("data-bs-container")}),this}getSidebarState(){const e=document.querySelector(".portal-container");return e?e.classList.contains("hide-sidebar")?"hidden":e.classList.contains("collapse-sidebar")?"collapsed":"normal":"normal"}isCollapsedState(){return"collapsed"===this.getSidebarState()}setToggleEnabled(e){return this.showToggle=e,this.render(),this}initializeMenus(e){if(e.menus)for(const t of e.menus)this.addMenu(t.name,t);else e.menu&&(e.menu.name=e.menu.name||"default",this.addMenu(e.menu.name,e.menu))}addTooltipHideListeners(){this._tooltipScrollHandler=()=>this.hideAllTooltips(),this.element.addEventListener("scroll",this._tooltipScrollHandler,{passive:!0}),this._tooltipRouteHandler=()=>this.hideAllTooltips(),this.getApp(),this._tooltipBlurHandler=()=>this.hideAllTooltips(),window.addEventListener("blur",this._tooltipBlurHandler),this._tooltipEscapeHandler=e=>{"Escape"===e.key&&this.hideAllTooltips()},document.addEventListener("keydown",this._tooltipEscapeHandler)}removeTooltipHideListeners(){this._tooltipScrollHandler&&(this.element.removeEventListener("scroll",this._tooltipScrollHandler),delete this._tooltipScrollHandler),this._tooltipBlurHandler&&(window.removeEventListener("blur",this._tooltipBlurHandler),delete this._tooltipBlurHandler),this._tooltipEscapeHandler&&(document.removeEventListener("keydown",this._tooltipEscapeHandler),delete this._tooltipEscapeHandler)}hideAllTooltips(){this.element.querySelectorAll('.sidebar-nav .nav-link[data-bs-toggle="tooltip"]').forEach(e=>{const t=e._tooltipInstance||window.bootstrap?.Tooltip?.getInstance(e);t&&t.hide()}),document.querySelectorAll(".tooltip.show").forEach(e=>{e.remove()})}async onBeforeDestroy(){this.destroyTooltips(),await super.onBeforeDestroy()}setupResponsiveBehavior(){const e=()=>{const e=window.innerWidth<=768,t=document.querySelector(".portal-container");t&&(e?t.classList.add("sidebar-mobile"):t.classList.remove("sidebar-mobile","sidebar-open"))};e(),window.addEventListener("resize",e)}static createDefault(e={}){return new Sidebar({theme:"sidebar-clean",showToggle:!0,autoCollapseMobile:!0,...e})}static createMinimal(e={}){return new Sidebar({theme:"sidebar-clean",showToggle:!1,autoCollapseMobile:!1,...e})}setSidebarTheme(e){return this.removeClass("sidebar-light sidebar-dark sidebar-clean"),this.sidebarTheme=e,this.addClass(e),this}show(){return this.setSidebarState("normal")}hide(){return this.setSidebarState("hidden")}collapse(){return this.setSidebarState("collapsed")}expand(){return this.setSidebarState("normal")}pulseToggle(){const e=this.element.querySelector(".sidebar-toggle");if(e){e.classList.add("pulse");const t=()=>{e.classList.remove("pulse"),e.removeEventListener("click",t)};e.addEventListener("click",t,{once:!0}),setTimeout(t,3e3)}return this}addSimpleMenuItem(e,t,s,i="bi-circle"){const a=this.menus.get(e);return a&&(a.items=a.items||[],a.items.push({text:t,route:s,icon:i}),this.activeMenuName===e&&this.render()),this}setSimpleMenu(e,t,s){const i={name:e,header:t,items:s};return this.addMenu(e,i),this.setActiveMenu(e),this}}class DeniedPage extends t.Page{constructor(e={}){super({pageName:"Access Denied",route:"/denied",title:"Access Denied",pageIcon:"bi bi-shield-x",template:'\n <div class="container mt-5">\n <div class="row justify-content-center">\n <div class="col-md-8 col-lg-6">\n <div class="text-center mb-4">\n <i class="bi bi-shield-x text-muted" style="font-size: 3rem;"></i>\n <h2 class="mt-3 mb-2">Access Denied</h2>\n <p class="text-muted">You don\'t have permission to access this page.</p>\n </div>\n\n {{#deniedPage}}\n <div class="card border-0 shadow-sm mb-4">\n <div class="card-body">\n <h6 class="card-subtitle mb-2 text-muted">Requested Page</h6>\n <h5 class="card-title">\n <i class="{{pageIcon}} me-2"></i>\n {{displayName}}\n </h5>\n {{#route}}\n <p class="card-text text-muted small">{{route}}</p>\n {{/route}}\n {{#description}}\n <p class="card-text">{{description}}</p>\n {{/description}}\n\n {{#requiredPermissions}}\n <div class="mt-3">\n <h6 class="mb-2">Required Permissions:</h6>\n {{#permissions}}\n <span class="badge bg-light text-dark me-1 mb-1">{{.}}</span>\n {{/permissions}}\n {{^permissions}}\n <span class="text-muted small">Authentication required</span>\n {{/permissions}}\n </div>\n {{/requiredPermissions}}\n </div>\n </div>\n {{/deniedPage}}\n\n <div class="d-grid gap-2 d-md-flex justify-content-md-center">\n <button type="button" class="btn btn-primary" data-action="go-back">\n <i class="bi bi-arrow-left me-1"></i>\n Go Back\n </button>\n <button type="button" class="btn btn-outline-secondary" data-action="go-home">\n <i class="bi bi-house me-1"></i>\n Home\n </button>\n {{#showLogin}}\n <button type="button" class="btn btn-outline-primary" data-action="login">\n <i class="bi bi-box-arrow-in-right me-1"></i>\n Login\n </button>\n {{/showLogin}}\n </div>\n\n {{#currentUser}}\n <div class="text-center mt-4">\n <small class="text-muted">\n Logged in as <strong>{{username}}</strong>\n </small>\n </div>\n {{/currentUser}}\n </div>\n </div>\n </div>\n ',...e}),this.deniedPage=null,this.deniedPageOptions=null}async onParams(e={},t={}){await super.onParams(e,t),e.page?(this.deniedPage=e.page,this.deniedPageOptions=e.page.options||e.page.pageOptions||{}):t.page&&(this.deniedPageName=t.page)}setDeniedPage(e){return this.deniedPage=e,this.deniedPageOptions=e?.options||e?.pageOptions||{},this}async getViewData(){const e=this.getApp(),t=e?.activeUser||e?.getCurrentUser?.()||null;let s=null;if(this.deniedPage){const e=this.deniedPageOptions?.permissions||this.deniedPage.options?.permissions||this.deniedPage.pageOptions?.permissions;s={displayName:this.deniedPage.displayName||this.deniedPage.pageName||this.deniedPage.title||"Unknown Page",pageName:this.deniedPage.pageName,route:this.deniedPage.route,description:this.deniedPage.pageDescription||this.deniedPage.description,pageIcon:this.deniedPage.pageIcon||"bi bi-file-text",requiredPermissions:e?{permissions:Array.isArray(e)?e:[e]}:null}}else this.deniedPageName&&(s={displayName:this.deniedPageName,pageName:this.deniedPageName,pageIcon:"bi bi-file-text"});return{deniedPage:s,currentUser:t?{username:t.username||t.name||t.email||"Unknown User",name:t.name,email:t.email}:null,showLogin:!t}}async handleActionGoBack(e,t){e.preventDefault(),window.history.length>1?window.history.back():await this.handleActionGoHome(e,t)}async handleActionGoHome(e,t){e.preventDefault();const s=this.getApp();s?await s.navigateToDefault():window.location.href="/"}async handleActionLogin(e,t){e.preventDefault();const s=this.getApp();if(s)try{await s.showPage("login")}catch(i){try{await s.navigate("/login")}catch(a){this.emit("login-required",{returnUrl:this.deniedPage?.route||window.location.pathname}),setTimeout(()=>{s?.showInfo?.("Please contact your administrator for access.")},100)}}}async onEnter(){await super.onEnter();const e=this.deniedPage?.pageName||this.deniedPageName;e&&this.setMeta({title:`Access Denied - ${e}`}),console.warn("Access denied to page:",{page:this.deniedPage?.pageName||this.deniedPageName,route:this.deniedPage?.route,permissions:this.deniedPageOptions?.permissions,timestamp:/* @__PURE__ */(new Date).toISOString()})}static showForPage(e,t){const s=new DeniedPage;return s.setDeniedPage(t),e.showPage(s)}}class NotFoundPage extends t.Page{constructor(e={}){super({pageName:"404",route:"/404",title:"404 - Page Not Found",pageIcon:"bi bi-search",template:'\n <div class="container mt-5">\n <div class="row justify-content-center">\n <div class="col-md-8 col-lg-6">\n <div class="text-center mb-4">\n <i class="bi bi-search text-muted" style="font-size: 3rem;"></i>\n <h2 class="mt-3 mb-2">Page Not Found</h2>\n <p class="text-muted">The page you\'re looking for doesn\'t exist.</p>\n </div>\n\n {{#path}}\n <div class="card border-0 shadow-sm mb-4">\n <div class="card-body text-center">\n <h6 class="card-subtitle mb-2 text-muted">Requested Path</h6>\n <code class="text-primary">{{path}}</code>\n </div>\n </div>\n {{/path}}\n\n <div class="d-grid gap-2 d-md-flex justify-content-md-center">\n <button type="button" class="btn btn-primary" data-action="go-back">\n <i class="bi bi-arrow-left me-1"></i>\n Go Back\n </button>\n <button type="button" class="btn btn-outline-secondary" data-action="go-home">\n <i class="bi bi-house me-1"></i>\n Home\n </button>\n </div>\n </div>\n </div>\n </div>\n ',...e}),this.path=null}async onParams(e={},t={}){await super.onParams(e,t),e.path&&(this.path=e.path),t.path&&(this.path=t.path)}setInfo(e){return this.path=e||null,this}async handleActionGoBack(e,t){e.preventDefault(),window.history.length>1?window.history.back():await this.handleActionGoHome(e,t)}async handleActionGoHome(e,t){e.preventDefault();const s=this.getApp();s?await s.navigateToDefault():window.location.href="/"}async onEnter(){await super.onEnter(),this.path&&this.setMeta({title:`404 - ${this.path} Not Found`}),console.warn("404 Not Found:",{path:this.path,timestamp:/* @__PURE__ */(new Date).toISOString()})}static showForPath(e,t){const s=new NotFoundPage;return s.setInfo(t),s.render()}}class PortalApp extends e.WebApp{constructor(e={}){super(e),this.sidebarConfig={},e.sidebar&&e.sidebar.menus?this.sidebarConfig.menus=e.sidebar.menus:e.sidebar.menu?this.sidebarConfig.menu=e.sidebar.menu:e.sidebar.items&&(this.sidebarConfig.menu=e.sidebar),this.topbarConfig=e.topbar||{},e.topnav&&!e.topbar&&(this.topbarConfig=e.topnav),this.sidebar=null,this.topbar=null,this.topnav=null,this.tokenManager=new n.TokenManager,this.activeGroup=null,this.isMobile()?this.sidebarCollapsed=this.sidebarConfig.defaultCollapsed||!1:this.sidebarCollapsed=this.loadSidebarState()??(this.sidebarConfig.defaultCollapsed||!1),this.setupPageContainer(),this.toast=new s.ToastService,this.Dialog=o.default,this.registerPage("denied",DeniedPage),this.registerPage("404",NotFoundPage)}async start(){await this.checkAuthStatus(),this.events.on("auth:unauthorized",()=>{this.showError("You have been logged out"),this.setActiveUser(null)}),this.events.on("auth:logout",()=>{this.showError("You have been logged out"),this.tokenManager.clearTokens(),this.setActiveUser(null)}),this.events.on("browser:focus",()=>{this.activeUser&&this.tokenManager.checkAndRefreshTokens(this)}),this.events.on("portal:action",this.onPortalAction.bind(this)),this.activeUser&&await this.checkActiveGroup(),console.log("Setting up router..."),await this.setupRouter(),this.isStarted=!0,this.events.emit("app:ready",{app:this}),console.log(`${this.title} portal ready`)}async checkAuthStatus(){const e=this.tokenManager.checkTokenStatus();if("logout"===e.action)return this.events.emit("auth:unauthorized",{app:this}),!1;if("refresh"===e.action&&!(await this.tokenManager.checkAndRefreshTokens(this)))return!1;const t=this.tokenManager.getTokenInstance();if(this.activeUser)return this.tokenManager.startAutoRefresh(this),!0;this.rest.setAuthToken(t.token);const i=new s.User({id:t.getUserId()}),a=await i.fetch();return a.success?(this.setActiveUser(i),this.tokenManager.startAutoRefresh(this),!0):(this.tokenManager.clearTokens(),this.events.emit("auth:unauthorized",{app:this,error:a.error}),!1)}async checkActiveGroup(){const e=new URLSearchParams(window.location.search).get("group"),t=e||this.loadActiveGroupId();if(t)try{const a=new s.Group({id:t}),n=await a.fetch();if(!n.success||!n.data.status)return this.clearActiveGroup(),void console.warn("Failed to load active group:",n.statusText);this.activeGroup=a,e&&this.saveActiveGroupId(t),this.activeUser&&(this.activeUser.member=new i.Member,await this.activeUser.member.fetchForGroup(a.id)),console.log("Loaded active group:",a.get("name"))}catch(a){if(console.warn("Failed to load active group:",a),e&&!this.loadActiveGroupId())this.clearActiveGroupId();else if(e){const t=this.loadActiveGroupId();if(t&&t!==e)try{const e=new s.Group({id:t});await e.fetch(),this.activeGroup=e,console.log("Fell back to stored active group:",e.get("name"))}catch(n){console.warn("Fallback to stored group also failed:",n),this.clearActiveGroupId()}}}}async setActiveGroup(e){const t=this.activeGroup;this.activeGroup=e,e&&e.get("id")?this.saveActiveGroupId(e.get("id")):this.clearActiveGroupId(),this.activeUser&&(this.activeUser.member=new i.Member,await this.activeUser.member.fetchForGroup(e.id)),this.events.emit("group:changed",{group:e,previousGroup:t,app:this});const s=this.getCurrentPage();return s&&s.onGroupChange&&s.onGroupChange(e),this.router.updateUrl({group:e.id},{replace:!0}),console.log("Active group set to:",e?e.get("name"):"none"),this}getActiveGroup(){return this.activeGroup}async clearActiveGroup(){const e=this.activeGroup;return this.activeGroup=null,this.clearActiveGroupId(),this.events.emit("group:cleared",{previousGroup:e,app:this}),this}saveActiveGroupId(e){try{const t=this.getActiveGroupStorageKey();localStorage.setItem(t,e.toString())}catch(t){console.warn("Failed to save active group ID:",t)}}loadActiveGroupId(){try{const e=this.getActiveGroupStorageKey();return localStorage.getItem(e)}catch(e){return console.warn("Failed to load active group ID:",e),null}}clearActiveGroupId(){try{const e=this.getActiveGroupStorageKey();localStorage.removeItem(e)}catch(e){console.warn("Failed to clear active group ID:",e)}}getActiveGroupStorageKey(){return"mojo_active_group_id"}setPortalProfile(e){try{localStorage.setItem("mojo_portal_profile",e)}catch(t){console.warn("Failed to save portal profile:",t)}}needsGroupSelection(){return!this.activeGroup}setupPageContainer(){const e="string"==typeof this.container?document.querySelector(this.container):this.container;if(!e)throw new Error(`Portal container not found: ${this.container}`);const t=this.sidebarConfig&&Object.keys(this.sidebarConfig).length>0,s=this.topbarConfig&&Object.keys(this.topbarConfig).length>0;e.innerHTML=`\n <div class="portal-layout hide-sidebar">\n ${t?'<div id="portal-sidebar"></div>':""}\n <div class="portal-body">\n ${s?'<div id="portal-topnav"></div>':""}\n <div class="portal-content" id="page-container">\n \x3c!-- Pages render here --\x3e\n </div>\n </div>\n </div>\n `,this.pageContainer="#page-container",e.classList.add("portal-container"),this.setupPortalComponents(),this.applySidebarState(e)}async setupPortalComponents(){await this.setupSidebar(),await this.setupTopbar(),this.setupPortalEvents()}async setupSidebar(){this.sidebarConfig&&0!==Object.keys(this.sidebarConfig).length&&(this.sidebar=new Sidebar({containerId:"portal-sidebar",...this.sidebarConfig}),await this.sidebar.render())}async setupTopbar(){this.topbarConfig&&0!==Object.keys(this.topbarConfig).length&&(this.topbar=new a.TopNav({containerId:"portal-topnav",brandText:this.topbarConfig.brand||this.brand||this.title,brandRoute:this.topbarConfig.brandRoute||"/",brandIcon:this.topbarConfig.brandIcon||this.brandIcon,navItems:this.topbarConfig.leftItems||[],rightItems:this.topbarConfig.rightItems||[],displayMode:this.topbarConfig.displayMode||"both",showSidebarToggle:this.topbarConfig.showSidebarToggle||!1,...this.topbarConfig}),await this.topbar.render(),this.topnav=this.topbar)}setupPortalEvents(){if(document.addEventListener("click",e=>{e.target.closest('[data-action="toggle-sidebar"]')&&(e.preventDefault(),this.toggleSidebar())}),window.ResizeObserver){const e=new ResizeObserver(()=>{this.handleResponsive()});e.observe(document.body),this._resizeObserver=e}else this._resizeHandler=()=>this.handleResponsive(),window.addEventListener("resize",this._resizeHandler);this.handleResponsive()}toggleSidebar(){if(!this.sidebar)return;const e=document.querySelector(".portal-container"),t=this.isMobile();t?e.classList.toggle("hide-sidebar"):(e.classList.toggle("collapse-sidebar"),this.sidebarCollapsed=!this.sidebarCollapsed,this.saveSidebarState(this.sidebarCollapsed)),this.events.emit("sidebar:toggled",{collapsed:this.sidebarCollapsed,mobile:t})}handleResponsive(){const e=document.querySelector(".portal-container");if(!e)return;const t=this.isMobile();t?(e.classList.add("mobile-layout"),e.classList.contains("hide-sidebar")||e.classList.add("hide-sidebar")):e.classList.remove("mobile-layout","hide-sidebar"),this.events.emit("responsive:changed",{mobile:t})}getPortalContainer(){return document.querySelector(".portal-container")}isMobile(){return window.innerWidth<768}hasMobileLayout(){return this.getPortalContainer().classList.contains("mobile-layout")}async showPage(e,t={},s={},i={}){const a=await super.showPage(e,t,s,i);return this.hasMobileLayout()&&this.getPortalContainer().classList.add("hide-sidebar"),a&&this.currentPageInstance&&this.updateNavigation(this.currentPageInstance),a}updateNavigation(e){this.sidebar&&this.sidebar.setActivePage&&this.sidebar.setActivePage(e.route),this.topbar&&this.topbar.setActivePage&&this.topbar.setActivePage(e.route),this.events.emit("portal:page-changed",{page:e})}setActiveUser(e){this.activeUser=e,this.topbar&&this.topbar.setUser(e),console.log("Active user set:",e),this.events.emit("portal:user-changed",{user:e})}getActiveUser(){return this.activeUser}saveSidebarState(e){try{const t=this.getSidebarStorageKey();localStorage.setItem(t,JSON.stringify(e))}catch(t){console.warn("Failed to save sidebar state:",t)}}loadSidebarState(){try{const e=this.getSidebarStorageKey(),t=localStorage.getItem(e);return null!==t?JSON.parse(t):null}catch(e){return console.warn("Failed to load sidebar state:",e),null}}getSidebarStorageKey(){return`${this.title?this.title.replace(/\s+/g,"_").toLowerCase():"portal_app"}_sidebar_collapsed`}applySidebarState(e=null){e||(e=document.querySelector(".portal-container")),e&&(this.sidebarCollapsed?e.classList.add("collapse-sidebar"):e.classList.remove("collapse-sidebar"))}clearSidebarState(){try{const e=this.getSidebarStorageKey();localStorage.removeItem(e)}catch(e){console.warn("Failed to clear sidebar state:",e)}}onPortalAction(e){switch(e.action){case"logout":this.showError("You have been logged out"),this.tokenManager.clearTokens(),this.setActiveUser(null);break;case"profile":this.showProfile();break;default:console.warn(`Unknown portal action: ${e}`)}}async showProfile(){if(this.activeUser)try{this.activeUser?.attributes&&console.log("activeUser.attributes:",this.activeUser.attributes);const e=await o.default.showModelForm({title:"Edit Profile",size:"lg",fileHandling:"base64",model:this.activeUser,fields:[{type:"header",text:"Profile Information",level:4,class:"text-primary mb-3"},{type:"group",columns:{xs:12,md:4},title:"Avatar",fields:[{type:"image",name:"avatar",size:"lg",imageSize:{width:200,height:200},placeholder:"Upload your avatar",help:"Square images work best"}]},{type:"group",columns:{xs:12,md:8},title:"Details",fields:[{type:"text",name:"display_name",label:"Display Name",required:!0,columns:12,placeholder:"Enter first name"},{type:"email",name:"email",label:"Email Address",required:!0,columns:8,placeholder:"your.email@example.com"},{type:"tel",name:"phone_number",label:"Phone Number",columns:4,placeholder:"(555) 123-4567"}]},{type:"group",columns:12,title:"Account Settings",class:"pt-3",fields:[{type:"select",name:"timezone",label:"Timezone",columns:6,options:[{value:"America/New_York",text:"Eastern Time"},{value:"America/Chicago",text:"Central Time"},{value:"America/Denver",text:"Mountain Time"},{value:"America/Los_Angeles",text:"Pacific Time"},{value:"UTC",text:"UTC"}]},{type:"select",name:"language",label:"Language",columns:6,options:[{value:"en",text:"English"},{value:"es",text:"Spanish"},{value:"fr",text:"French"},{value:"de",text:"German"}]},{type:"switch",name:"email_notifications",label:"Email Notifications",columns:4},{type:"switch",name:"two_factor_enabled",label:"Two-Factor Authentication",columns:4},{type:"switch",name:"profile_public",label:"Public Profile",columns:4}]}],submitText:"Save Profile",cancelText:"Cancel"});e&&e.success?(console.log("Profile saved successfully:",e),this.showSuccess("Profile updated successfully!")):e&&!e.success&&console.log("Profile save failed:",e)}catch(e){console.error("Error showing profile form:",e),this.showError("Failed to load profile form")}else this.showError("No user is currently logged in")}async destroy(){this.activeGroup=null,this._resizeObserver&&this._resizeObserver.disconnect(),this._resizeHandler&&window.removeEventListener("resize",this._resizeHandler),this.topbar&&(await this.topbar.destroy(),this.topbar=null,this.topnav=null),this.sidebar&&(await this.sidebar.destroy(),this.sidebar=null),await super.destr