import * as plugins from '../plugins.js'; import * as appstate from '../appstate.js'; import * as interfaces from '../../ts_interfaces/index.js'; import { DeesElement, customElement, html, state, css, cssManager, type TemplateResult, } from '@design.estate/dees-element'; import type { GitopsViewOverview } from './views/overview/index.js'; import type { GitopsViewConnections } from './views/connections/index.js'; import type { GitopsViewProjects } from './views/projects/index.js'; import type { GitopsViewGroups } from './views/groups/index.js'; import type { GitopsViewSecrets } from './views/secrets/index.js'; import type { GitopsViewPipelines } from './views/pipelines/index.js'; import type { GitopsViewBuildlog } from './views/buildlog/index.js'; @customElement('gitops-dashboard') export class GitopsDashboard 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('./views/overview/index.js')).GitopsViewOverview)() }, { name: 'Connections', iconName: 'lucide:plug', element: (async () => (await import('./views/connections/index.js')).GitopsViewConnections)() }, { name: 'Projects', iconName: 'lucide:folderGit2', element: (async () => (await import('./views/projects/index.js')).GitopsViewProjects)() }, { name: 'Groups', iconName: 'lucide:users', element: (async () => (await import('./views/groups/index.js')).GitopsViewGroups)() }, { name: 'Secrets', iconName: 'lucide:key', element: (async () => (await import('./views/secrets/index.js')).GitopsViewSecrets)() }, { name: 'Pipelines', iconName: 'lucide:play', element: (async () => (await import('./views/pipelines/index.js')).GitopsViewPipelines)() }, { name: 'Build Log', iconName: 'lucide:scrollText', element: (async () => (await import('./views/buildlog/index.js')).GitopsViewBuildlog)() }, ]; private resolvedViewTabs: Array<{ name: string; iconName: string; element: any }> = []; constructor() { super(); document.title = 'GitOps'; const loginSubscription = appstate.loginStatePart .select((stateArg) => stateArg) .subscribe((loginState) => { this.loginState = loginState; if (loginState.isLoggedIn) { appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, 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(); appstate.uiStatePart.dispatchAction(appstate.setActiveViewAction, { view: viewName }); }); appDash.addEventListener('logout', async () => { await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null); }); } // Load initial view on appdash 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_VerifyIdentity >('/typedrequest', 'verifyIdentity'); const response = await typedRequest.fire({ identity: loginState.identity }); if (response.valid) { this.loginState = loginState; if (simpleLogin) { await simpleLogin.switchToSlottedContent(); } } else { await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null); } } 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(); } } 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); } }