import * as plugins from '../plugins.js'; import * as appstate from '../appstate.js'; import * as interfaces from '../../ts_interfaces/index.js'; import { appRouter } from '../router.js'; import { DeesElement, customElement, html, state, css, cssManager, type TemplateResult, } from '@design.estate/dees-element'; import type { ObjstViewOverview } from './objst-view-overview.js'; import type { ObjstViewBuckets } from './objst-view-buckets.js'; import type { ObjstViewObjects } from './objst-view-objects.js'; import type { ObjstViewPolicies } from './objst-view-policies.js'; import type { ObjstViewConfig } from './objst-view-config.js'; import type { ObjstViewCredentials } from './objst-view-credentials.js'; @customElement('objst-app-shell') export class ObjstAppShell extends DeesElement { @state() accessor loginState: appstate.ILoginState = { identity: null, isLoggedIn: false }; @state() accessor uiState: appstate.IUiState = { activeView: 'overview', autoRefresh: true, refreshInterval: 30000, }; private viewTabs = [ { name: 'Overview', iconName: 'lucide:layoutDashboard', element: (async () => (await import('./objst-view-overview.js')).ObjstViewOverview)() }, { name: 'Buckets', iconName: 'lucide:database', element: (async () => (await import('./objst-view-buckets.js')).ObjstViewBuckets)() }, { name: 'Browser', iconName: 'lucide:folderOpen', element: (async () => (await import('./objst-view-objects.js')).ObjstViewObjects)() }, { name: 'Policies', iconName: 'lucide:shield', element: (async () => (await import('./objst-view-policies.js')).ObjstViewPolicies)() }, { name: 'Config', iconName: 'lucide:settings', element: (async () => (await import('./objst-view-config.js')).ObjstViewConfig)() }, { name: 'Access Keys', iconName: 'lucide:key', element: (async () => (await import('./objst-view-credentials.js')).ObjstViewCredentials)() }, ]; private resolvedViewTabs: Array<{ name: string; iconName: string; element: any }> = []; constructor() { super(); document.title = 'ObjectStorage'; const loginSubscription = appstate.loginStatePart .select((stateArg) => stateArg) .subscribe((loginState) => { this.loginState = loginState; if (loginState.isLoggedIn) { appstate.serverStatePart.dispatchAction(appstate.fetchServerStatusAction, null); } }); this.rxSubscriptions.push(loginSubscription); const uiSubscription = appstate.uiStatePart .select((stateArg) => stateArg) .subscribe((uiState) => { this.uiState = uiState; this.syncAppdashView(uiState.activeView); }); this.rxSubscriptions.push(uiSubscription); } public static styles = [ cssManager.defaultStyles, css` :host { display: block; width: 100%; height: 100%; } .maincontainer { width: 100%; height: 100vh; } `, ]; public render(): TemplateResult { return html`
`; } public async firstUpdated() { // Resolve async view tab imports this.resolvedViewTabs = await Promise.all( this.viewTabs.map(async (tab) => ({ name: tab.name, iconName: tab.iconName, element: await tab.element, })), ); this.requestUpdate(); await this.updateComplete; const simpleLogin = this.shadowRoot!.querySelector('dees-simple-login') as any; if (simpleLogin) { simpleLogin.addEventListener('login', (e: CustomEvent) => { this.login(e.detail.data.username, e.detail.data.password); }); } const appDash = this.shadowRoot!.querySelector('dees-simple-appdash') as any; if (appDash) { appDash.addEventListener('view-select', (e: CustomEvent) => { const viewName = e.detail.view.name.toLowerCase(); appRouter.navigateToView(viewName); }); appDash.addEventListener('logout', async () => { await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null); }); } // Load the initial view on the appdash now that tabs are resolved if (appDash && this.resolvedViewTabs.length > 0) { const initialView = this.resolvedViewTabs.find( (t) => t.name.toLowerCase() === this.uiState.activeView, ) || this.resolvedViewTabs[0]; await appDash.loadView(initialView); } // Check for stored session (persistent login state) const loginState = appstate.loginStatePart.getState(); if (loginState.identity?.jwt) { if (loginState.identity.expiresAt > Date.now()) { try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServerStatus >('/typedrequest', 'getServerStatus'); const response = await typedRequest.fire({ identity: loginState.identity }); appstate.serverStatePart.setState({ status: response.status, connectionInfo: response.connectionInfo, }); this.loginState = loginState; if (simpleLogin) { await simpleLogin.switchToSlottedContent(); } } catch (err) { console.warn('Stored session invalid, returning to login:', err); await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null); } } else { await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null); } } } private async login(username: string, password: string) { const domtools = await this.domtoolsPromise; const simpleLogin = this.shadowRoot!.querySelector('dees-simple-login') as any; const form = simpleLogin?.shadowRoot?.querySelector('dees-form') as any; if (form) { form.setStatus('pending', 'Logging in...'); } const newState = await appstate.loginStatePart.dispatchAction(appstate.loginAction, { username, password, }); if (newState.identity) { if (form) { form.setStatus('success', 'Logged in!'); } if (simpleLogin) { await simpleLogin.switchToSlottedContent(); } await appstate.serverStatePart.dispatchAction(appstate.fetchServerStatusAction, null); } else { if (form) { form.setStatus('error', 'Login failed!'); await domtools.convenience.smartdelay.delayFor(2000); form.reset(); } } } private syncAppdashView(viewName: string): void { const appDash = this.shadowRoot?.querySelector('dees-simple-appdash') as any; if (!appDash || this.resolvedViewTabs.length === 0) return; const targetTab = this.resolvedViewTabs.find((t) => t.name.toLowerCase() === viewName); if (!targetTab) return; appDash.loadView(targetTab); } }