import * as plugins from '../../plugins.js'; import * as states from '../../states/accountstate.js'; import { IdpState } from '../../states/idp.state.js'; import { BulkInviteModal } from './bulk-invite-modal.js'; import { CreateOrgModal } from './create-org-modal.js'; import { customElement, DeesElement, html, cssManager, css, state, type TemplateResult } from '@design.estate/dees-element'; declare global { interface HTMLElementTagNameMap { 'idp-accountcontent': IdpAccountContent; } } @customElement('idp-accountcontent') export class IdpAccountContent extends DeesElement { public subrouter: plugins.deesDomtools.plugins.smartrouter.SmartRouter; private dataLoadRun = 0; @state() private accessor adminPage: plugins.idpCatalog.IdpAdminShell['page'] = 'overview'; @state() private accessor adminUser: plugins.idpCatalog.IIdpAdminUser = { name: 'Loading account', email: '', }; @state() private accessor adminOrgs: plugins.idpCatalog.IIdpAdminOrg[] = []; @state() private accessor selectedOrgId = ''; @state() private accessor globalAdmin = false; @state() private accessor dataLoading = false; @state() private accessor dataError = ''; @state() private accessor sessions: plugins.idpCatalog.IIdpAdminSession[] = []; @state() private accessor activities: plugins.idpCatalog.IIdpAdminActivity[] = []; @state() private accessor orgMembers: plugins.idpCatalog.IIdpAdminMember[] = []; @state() private accessor orgInvitations: plugins.idpCatalog.IIdpAdminInvitation[] = []; @state() private accessor orgRoleDefinitions: plugins.idpCatalog.IIdpAdminOrgRoleDefinition[] = []; @state() private accessor orgApps: plugins.idpCatalog.IIdpAdminApp[] = []; @state() private accessor adminApps: plugins.idpCatalog.IIdpAdminApp[] = []; @state() private accessor passportDevices: plugins.idpCatalog.IIdpAdminPassportDevice[] = []; @state() private accessor passportEnrollment: plugins.idpCatalog.IIdpAdminPassportEnrollment | null = null; @state() private accessor credentialMessage = ''; constructor() { super(); } public static styles = [ cssManager.defaultStyles, css` :host { display: block; height: 100vh; max-height: 100vh; min-height: 0; width: 100%; overflow: hidden; background: var(--idp-bg, hsl(240 10% 3.9%)); } :host([hidden]) { display: none; } idp-admin-shell { height: 100%; } `, ]; public render(): TemplateResult { return html` `; } private setAdminPage(pageArg: plugins.idpCatalog.IdpAdminShell['page']) { this.adminPage = pageArg; if (this.subrouter) { void this.loadAdminShellData(); } } private getSelectedOrgSlug(): string { const currentState = states.accountState.getState(); const selectedOrg = currentState.selectedOrg || currentState.organizations.find((orgArg) => orgArg.id === this.selectedOrgId) || currentState.organizations[0]; return selectedOrg?.data?.slug || this.adminOrgs.find((orgArg) => orgArg.id === this.selectedOrgId)?.slug || this.adminOrgs[0]?.slug || ''; } private getPathForPage(pageArg: plugins.idpCatalog.IdpAdminShell['page']): string | null { const orgSlug = this.getSelectedOrgSlug(); const orgPath = (suffixArg = '') => orgSlug ? `/org/${orgSlug}${suffixArg}` : null; const pageMap: Record = { overview: '/overview', profile: '/account/profile', security: '/account/security', sessions: '/account/sessions', apps: '/account/apps', 'org-general': orgPath(), 'org-settings': orgPath('/settings'), 'org-members': orgPath('/users'), 'org-apps': orgPath('/apps'), support: '/support', 'ga-users': '/admin/users', 'ga-orgs': '/admin/orgs', 'ga-apps': '/admin/apps', }; return pageMap[pageArg]; } private pushDashPath(pathArg: string) { const normalizedPath = pathArg || ''; const absolutePath = `/dash${normalizedPath}`.replace(/\/$/, '') || '/dash'; if (window.location.pathname.replace(/\/$/, '') === absolutePath) { return; } this.subrouter.pushUrl(normalizedPath); } private async handleAdminNavigate(eventArg: CustomEvent) { const page = eventArg.detail.page; this.setAdminPage(page); const path = this.getPathForPage(page); if (path !== null) { this.pushDashPath(path); } } private async handleOrgSelect(eventArg: CustomEvent) { const currentState = states.accountState.getState(); const selectedOrg = currentState.organizations.find((orgArg) => orgArg.id === eventArg.detail.orgId) || currentState.organizations.find((orgArg) => orgArg.data.slug === eventArg.detail.org?.slug); this.selectedOrgId = eventArg.detail.orgId; this.setAdminPage('org-general'); if (selectedOrg) { await states.accountState.dispatchAction(states.setSelectedOrg, selectedOrg); this.pushDashPath(`/org/${selectedOrg.data.slug}`); } else if (eventArg.detail.org?.slug) { this.pushDashPath(`/org/${eventArg.detail.org.slug}`); } } private async handleOrgCreate() { const org = await CreateOrgModal.show(); if (!org) { return; } this.applyAccountState(); this.selectedOrgId = org.id; this.setAdminPage('org-general'); this.pushDashPath(`/org/${org.data.slug}`); } private async handleOrgUpdate(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('updateOrganization'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: eventArg.detail.organizationId, name: eventArg.detail.name, slug: eventArg.detail.slug, confirmationText: eventArg.detail.confirmationText, }); if (!response.success) { throw new Error(response.message || 'Organization update failed.'); } await states.accountState.dispatchAction(states.getOrganizationsAction, null); const refreshedOrg = states.accountState.getState().organizations.find((orgArg) => orgArg.id === response.organization.id) || response.organization; await states.accountState.dispatchAction(states.setSelectedOrg, refreshedOrg); this.applyAccountState(); this.selectedOrgId = refreshedOrg.id; this.setAdminPage('org-settings'); this.pushDashPath(`/org/${refreshedOrg.data.slug}/settings`); }); } private async handleOrgTransfer(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('transferOwnership'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: eventArg.detail.organizationId, newOwnerId: eventArg.detail.newOwnerId, confirmationText: eventArg.detail.confirmationText, }); if (!response.success) { throw new Error(response.message || 'Ownership transfer failed.'); } await states.accountState.dispatchAction(states.getOrganizationsAction, null); const refreshedOrg = states.accountState.getState().organizations.find((orgArg) => orgArg.id === eventArg.detail.organizationId); if (refreshedOrg) { await states.accountState.dispatchAction(states.setSelectedOrg, refreshedOrg); this.selectedOrgId = refreshedOrg.id; } this.applyAccountState(); this.setAdminPage('org-settings'); }); } private async handleOrgDelete(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('deleteOrganization'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: eventArg.detail.organizationId, confirmationText: eventArg.detail.confirmationText, }); if (!response.success) { throw new Error(response.message || 'Organization deletion failed.'); } await states.accountState.dispatchAction(states.getOrganizationsAction, null); const nextOrg = states.accountState.getState().organizations[0] || null; if (nextOrg) { await states.accountState.dispatchAction(states.setSelectedOrg, nextOrg); } else { await states.accountState.dispatchAction(states.setSelectedOrg, null as any); } this.selectedOrgId = nextOrg?.id || ''; this.applyAccountState(); this.setAdminPage('overview'); this.pushDashPath('/overview'); }); } private async syncSelectedOrgFromPath() { const orgSlug = window.location.pathname.match(/^\/dash\/org\/([^/]+)/)?.[1]; if (!orgSlug) { return; } const currentState = states.accountState.getState(); const selectedOrg = currentState.organizations.find((orgArg) => orgArg.data.slug === orgSlug); if (!selectedOrg) { return; } this.selectedOrgId = selectedOrg.id; if (currentState.selectedOrg?.id !== selectedOrg.id) { await states.accountState.dispatchAction(states.setSelectedOrg, selectedOrg); } } private applyAccountState() { const currentState = states.accountState.getState(); const user = currentState.user; if (user) { this.adminUser = { name: user.data.name || user.data.username || user.data.email, email: user.data.email, username: user.data.username, mobileNumber: user.data.mobileNumber, status: user.data.status, }; this.globalAdmin = Boolean(user.data.isGlobalAdmin); } this.adminOrgs = currentState.organizations.map((orgArg) => { const role = currentState.roles.find((roleArg) => roleArg.data.organizationId === orgArg.id); return { id: orgArg.id, name: orgArg.data.name, slug: orgArg.data.slug, myRole: role?.data.roles?.[0] || 'member', }; }); this.selectedOrgId = currentState.selectedOrg?.id || this.selectedOrgId || currentState.organizations[0]?.id || ''; const selectedOrg = currentState.organizations.find((orgArg) => orgArg.id === this.selectedOrgId) || currentState.selectedOrg || currentState.organizations[0]; this.orgRoleDefinitions = selectedOrg?.data.roleDefinitions || []; } private async setOrgPage(pageArg: plugins.idpCatalog.IdpAdminShell['page']) { await this.syncSelectedOrgFromPath(); this.setAdminPage(pageArg); } private getSelectedOrganization(): plugins.idpInterfaces.data.IOrganization | null { const currentState = states.accountState.getState(); return currentState.selectedOrg || currentState.organizations.find((orgArg) => orgArg.id === this.selectedOrgId) || currentState.organizations[0] || null; } private async loadSessions(idpStateArg: IdpState, jwtArg: string): Promise { const request = idpStateArg.idpClient.typedsocket.createTypedRequest('getUserSessions'); const response = await request.fire({ jwt: jwtArg }); return (response.sessions || []).map((sessionArg) => ({ id: sessionArg.id, deviceName: sessionArg.deviceName, browser: sessionArg.browser, os: sessionArg.os, ip: sessionArg.ip, lastActive: sessionArg.lastActive, createdAt: sessionArg.createdAt, isCurrent: sessionArg.isCurrent, })); } private async loadActivities(idpStateArg: IdpState, jwtArg: string): Promise { const request = idpStateArg.idpClient.typedsocket.createTypedRequest('getUserActivity'); const response = await request.fire({ jwt: jwtArg, limit: 20 }); return (response.activities || []).map((activityArg) => ({ id: activityArg.id, action: activityArg.data.action, description: activityArg.data.metadata.description, timestamp: activityArg.data.timestamp, ip: activityArg.data.metadata.ip, targetType: activityArg.data.metadata.targetType, })); } private async loadOrgMembers(idpStateArg: IdpState, jwtArg: string, organizationIdArg: string): Promise { const currentState = states.accountState.getState(); const request = idpStateArg.idpClient.typedsocket.createTypedRequest('getOrgMembers'); const response = await request.fire({ jwt: jwtArg, organizationId: organizationIdArg }); return (response.members || []).map((memberArg) => ({ userId: memberArg.user.id, name: memberArg.user.data.name || memberArg.user.data.username || memberArg.user.data.email, email: memberArg.user.data.email, roles: memberArg.role.data.roles || [], isCurrentUser: currentState.user?.id === memberArg.user.id, })); } private async loadOrgInvitations(idpStateArg: IdpState, jwtArg: string, organizationIdArg: string): Promise { const request = idpStateArg.idpClient.typedsocket.createTypedRequest('getOrgInvitations'); const response = await request.fire({ jwt: jwtArg, organizationId: organizationIdArg }); return (response.invitations || []).map((invitationArg) => { const orgRef = invitationArg.data.organizationRefs.find((refArg) => refArg.organizationId === organizationIdArg) || invitationArg.data.organizationRefs[0]; return { id: invitationArg.id, email: invitationArg.data.email, roles: orgRef?.roles || [], invitedAt: orgRef?.invitedAt || invitationArg.data.createdAt, expiresAt: invitationArg.data.expiresAt, status: invitationArg.data.status, }; }); } private async loadOrgApps(idpStateArg: IdpState, jwtArg: string, organizationIdArg: string): Promise { const appsRequest = idpStateArg.idpClient.typedsocket.createTypedRequest('getGlobalApps'); const connectionsRequest = idpStateArg.idpClient.typedsocket.createTypedRequest('getAppConnections'); const [appsResponse, connectionsResponse] = await Promise.all([ appsRequest.fire({ jwt: jwtArg }), connectionsRequest.fire({ jwt: jwtArg, organizationId: organizationIdArg }), ]); const activeConnectionMap = new Map((connectionsResponse.connections || []) .filter((connectionArg) => connectionArg.data.status === 'active') .map((connectionArg) => [connectionArg.data.appId, connectionArg])); return (appsResponse.apps || []).map((appArg) => ({ id: appArg.id, name: appArg.data.name, description: appArg.data.description, logoUrl: appArg.data.logoUrl, appUrl: appArg.data.appUrl, category: appArg.data.category, type: appArg.type, status: appArg.data.isActive ? 'active' : 'inactive', isConnected: activeConnectionMap.has(appArg.id), roleMappings: activeConnectionMap.get(appArg.id)?.data.roleMappings || [], clientId: appArg.data.oauthCredentials.clientId, scopes: activeConnectionMap.get(appArg.id)?.data.grantedScopes || appArg.data.oauthCredentials.allowedScopes || [], grants: appArg.data.oauthCredentials.grantTypes || [], })); } private async loadAdminApps(idpStateArg: IdpState, jwtArg: string): Promise { if (!this.globalAdmin) { return []; } const request = idpStateArg.idpClient.typedsocket.createTypedRequest('getGlobalAppStats'); const response = await request.fire({ jwt: jwtArg }); return (response.apps || []).map((entryArg) => ({ id: entryArg.app.id, name: entryArg.app.data.name, description: entryArg.app.data.description, logoUrl: entryArg.app.data.logoUrl, appUrl: entryArg.app.data.appUrl, category: entryArg.app.data.category, type: entryArg.app.type, status: entryArg.app.data.isActive ? 'active' : 'inactive', connectionCount: entryArg.connectionCount, clientId: entryArg.app.data.oauthCredentials.clientId, scopes: entryArg.app.data.oauthCredentials.allowedScopes || [], grants: entryArg.app.data.oauthCredentials.grantTypes || [], })); } private async loadPassportDevices(idpStateArg: IdpState, jwtArg: string): Promise { const request = idpStateArg.idpClient.typedsocket.createTypedRequest('getPassportDevices'); const response = await request.fire({ jwt: jwtArg }); return (response.devices || []).map((deviceArg) => ({ id: deviceArg.id, label: deviceArg.data.label, platform: deviceArg.data.platform, status: deviceArg.data.status, capabilities: deviceArg.data.capabilities, appVersion: deviceArg.data.appVersion, createdAt: deviceArg.data.createdAt, lastSeenAt: deviceArg.data.lastSeenAt, lastChallengeAt: deviceArg.data.lastChallengeAt, pushRegistered: Boolean(deviceArg.data.pushRegistration), })); } private async loadAdminShellData() { const currentRun = ++this.dataLoadRun; this.dataLoading = true; this.dataError = ''; try { const idpState = await IdpState.getSingletonInstance(); const jwt = await idpState.idpClient.getJwt(); const selectedOrg = this.getSelectedOrganization(); const orgId = selectedOrg?.id || ''; const [sessions, activities, members, invitations, orgApps, adminApps, passportDevices] = await Promise.all([ this.loadSessions(idpState, jwt).catch((error) => { console.error('Error loading sessions:', error); return this.sessions; }), this.loadActivities(idpState, jwt).catch((error) => { console.error('Error loading activity:', error); return this.activities; }), orgId ? this.loadOrgMembers(idpState, jwt, orgId).catch((error) => { console.error('Error loading org members:', error); return this.orgMembers; }) : Promise.resolve([]), orgId ? this.loadOrgInvitations(idpState, jwt, orgId).catch((error) => { console.error('Error loading org invitations:', error); return this.orgInvitations; }) : Promise.resolve([]), orgId ? this.loadOrgApps(idpState, jwt, orgId).catch((error) => { console.error('Error loading org apps:', error); return this.orgApps; }) : Promise.resolve([]), this.loadAdminApps(idpState, jwt).catch((error) => { console.error('Error loading admin apps:', error); return this.adminApps; }), this.loadPassportDevices(idpState, jwt).catch((error) => { console.error('Error loading passport devices:', error); return this.passportDevices; }), ]); if (currentRun !== this.dataLoadRun) { return; } this.sessions = sessions; this.activities = activities; this.orgMembers = members; this.orgInvitations = invitations; this.orgApps = orgApps; this.adminApps = adminApps; this.passportDevices = passportDevices; } catch (error) { console.error('Error loading admin shell data:', error); if (currentRun === this.dataLoadRun) { this.dataError = error instanceof Error ? error.message : 'Failed to load admin console data.'; } } finally { if (currentRun === this.dataLoadRun) { this.dataLoading = false; } } } private async runAdminAction(actionArg: () => Promise) { this.dataError = ''; try { await actionArg(); await this.loadAdminShellData(); } catch (error) { console.error('Admin console action failed:', error); this.dataError = error instanceof Error ? error.message : 'Action failed. Please try again.'; } } private async handleSessionRevoke(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('revokeSession'); await request.fire({ jwt: await idpState.idpClient.getJwt(), sessionId: eventArg.detail.sessionId }); }); } private async handleAppToggle(eventArg: CustomEvent) { const selectedOrg = this.getSelectedOrganization(); if (!selectedOrg) { this.dataError = 'Select an organisation before changing app connections.'; return; } await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('toggleAppConnection'); await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, appId: eventArg.detail.appId, action: eventArg.detail.connected ? 'connect' : 'disconnect', }); }); } private async handlePasswordChange(eventArg: CustomEvent) { const email = states.accountState.getState().user?.data.email; if (!email) { this.credentialMessage = ''; this.dataError = 'Cannot change password before account data is loaded.'; return; } await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('setNewPassword'); const response = await request.fire({ email, oldPassword: eventArg.detail.currentPassword, newPassword: eventArg.detail.newPassword, }); if (response.status !== 'ok') { throw new Error('Password change failed.'); } this.credentialMessage = 'Password changed successfully.'; }); } private async handlePassportEnroll(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('createPassportEnrollmentChallenge'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), deviceLabel: eventArg.detail.deviceLabel, platform: 'web', capabilities: { gps: false, nfc: false, push: false, }, }); this.passportEnrollment = response; this.credentialMessage = 'Passport enrollment challenge created.'; }); } private async handlePassportRevoke(eventArg: CustomEvent) { const device = this.passportDevices.find((deviceArg) => deviceArg.id === eventArg.detail.deviceId); if (!device || !confirm(`Revoke passport device ${device.label}?`)) { return; } await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('revokePassportDevice'); await request.fire({ jwt: await idpState.idpClient.getJwt(), deviceId: eventArg.detail.deviceId, }); this.credentialMessage = 'Passport device revoked.'; }); } private async handleMemberInvite() { const selectedOrg = this.getSelectedOrganization(); if (!selectedOrg) { this.dataError = 'Select an organisation before inviting members.'; return; } const result = await BulkInviteModal.show({ organizationId: selectedOrg.id, organizationName: selectedOrg.data.name, }); if (result?.invitedCount) { await this.loadAdminShellData(); } } private async handleMemberRemove(eventArg: CustomEvent) { const selectedOrg = this.getSelectedOrganization(); const member = this.orgMembers.find((memberArg) => memberArg.userId === eventArg.detail.userId); if (!selectedOrg || !member || !confirm(`Remove ${member.name} from ${selectedOrg.data.name}?`)) { return; } await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('removeMember'); await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, userId: member.userId }); }); } private async handleMemberRolesUpdate(eventArg: CustomEvent) { const selectedOrg = this.getSelectedOrganization(); if (!selectedOrg) { this.dataError = 'Select an organisation before editing member roles.'; return; } await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('updateMemberRoles'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, userId: eventArg.detail.userId, roles: eventArg.detail.roles, }); if (!response.success) { throw new Error(response.message || 'Member role update failed.'); } await states.accountState.dispatchAction(states.getOrganizationsAction, null); this.applyAccountState(); }); } private async handleOrgRoleUpsert(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('upsertOrgRoleDefinition'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: eventArg.detail.organizationId, roleDefinition: eventArg.detail.roleDefinition, }); if (!response.success) { throw new Error(response.message || 'Organization role update failed.'); } await states.accountState.dispatchAction(states.getOrganizationsAction, null); this.applyAccountState(); }); } private async handleOrgRoleDelete(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('deleteOrgRoleDefinition'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: eventArg.detail.organizationId, roleKey: eventArg.detail.roleKey, confirmationText: eventArg.detail.confirmationText, }); if (!response.success) { throw new Error(response.message || 'Organization role delete failed.'); } await states.accountState.dispatchAction(states.getOrganizationsAction, null); this.applyAccountState(); }); } private async handleAppRoleMappingsUpdate(eventArg: CustomEvent) { await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('updateAppRoleMappings'); const response = await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: eventArg.detail.organizationId, appId: eventArg.detail.appId, roleMappings: eventArg.detail.roleMappings, }); if (!response.success) { throw new Error(response.message || 'App role mapping update failed.'); } }); } private async handleInvitationResend(eventArg: CustomEvent) { const selectedOrg = this.getSelectedOrganization(); if (!selectedOrg) { return; } await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('resendInvitation'); await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, invitationId: eventArg.detail.invitationId }); }); } private async handleInvitationCancel(eventArg: CustomEvent) { const selectedOrg = this.getSelectedOrganization(); if (!selectedOrg || !confirm('Cancel this invitation?')) { return; } await this.runAdminAction(async () => { const idpState = await IdpState.getSingletonInstance(); const request = idpState.idpClient.typedsocket.createTypedRequest('cancelInvitation'); await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, invitationId: eventArg.detail.invitationId }); }); } public async firstUpdated(_changedProperties: Map): Promise { super.firstUpdated(_changedProperties); await this.domtoolsPromise; this.subrouter = this.domtools.router.createSubRouter('/dash'); await states.accountState.dispatchAction(states.getOrganizationsAction, null); this.applyAccountState(); this.subrouter.on('', async () => { this.pushDashPath('/overview'); }); this.subrouter.on('/overview', async () => { this.setAdminPage('overview'); }); this.subrouter.on('/account/profile', async () => { this.setAdminPage('profile'); }); this.subrouter.on('/account/security', async () => { this.setAdminPage('security'); }); this.subrouter.on('/account/sessions', async () => { this.setAdminPage('sessions'); }); this.subrouter.on('/account/apps', async () => { this.setAdminPage('apps'); }); this.subrouter.on('/support', async () => { this.setAdminPage('support'); }); this.subrouter.on('/org/:orgName', async () => { await this.setOrgPage('org-general'); }); this.subrouter.on('/org/:orgName/settings', async () => { await this.setOrgPage('org-settings'); }); this.subrouter.on('/org/:orgName/apps', async () => { await this.setOrgPage('org-apps'); }); this.subrouter.on('/org/:orgName/users', async () => { await this.setOrgPage('org-members'); }); this.subrouter.on('/admin', async () => { this.pushDashPath('/admin/apps'); }); this.subrouter.on('/admin/users', async () => { this.setAdminPage('ga-users'); }); this.subrouter.on('/admin/orgs', async () => { this.setAdminPage('ga-orgs'); }); this.subrouter.on('/admin/apps', async () => { this.setAdminPage('ga-apps'); }); this.subrouter._handleRouteState(); states.accountState.select((stateArg) => stateArg.user).subscribe(() => this.applyAccountState()); states.accountState.select((stateArg) => stateArg.organizations).subscribe(() => this.applyAccountState()); states.accountState.select((stateArg) => stateArg.roles).subscribe(() => this.applyAccountState()); states.accountState.select((stateArg) => stateArg.selectedOrg).subscribe(() => this.applyAccountState()); this.registerGarbageFunction(async () => { this.subrouter.destroy(); }) } }