2024-10-06 23:56:03 +02:00
|
|
|
import * as plugins from '../../plugins.js';
|
2026-05-07 15:35:37 +00:00
|
|
|
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';
|
2024-10-06 23:56:03 +02:00
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
customElement,
|
|
|
|
|
DeesElement,
|
|
|
|
|
html,
|
|
|
|
|
cssManager,
|
|
|
|
|
css,
|
2026-05-07 15:35:37 +00:00
|
|
|
state,
|
2024-10-06 23:56:03 +02:00
|
|
|
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;
|
2026-05-07 15:35:37 +00:00
|
|
|
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 = '';
|
2024-10-06 23:56:03 +02:00
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static styles = [
|
|
|
|
|
cssManager.defaultStyles,
|
|
|
|
|
css`
|
|
|
|
|
:host {
|
|
|
|
|
display: block;
|
2026-05-07 15:35:37 +00:00
|
|
|
height: 100vh;
|
|
|
|
|
max-height: 100vh;
|
|
|
|
|
min-height: 0;
|
2024-10-06 23:56:03 +02:00
|
|
|
width: 100%;
|
2026-05-07 15:35:37 +00:00
|
|
|
overflow: hidden;
|
|
|
|
|
background: var(--idp-bg, hsl(240 10% 3.9%));
|
2024-10-06 23:56:03 +02:00
|
|
|
}
|
|
|
|
|
:host([hidden]) {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
2026-05-07 15:35:37 +00:00
|
|
|
idp-admin-shell {
|
2024-10-06 23:56:03 +02:00
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public render(): TemplateResult {
|
|
|
|
|
return html`
|
2026-05-07 15:35:37 +00:00
|
|
|
<idp-admin-shell
|
|
|
|
|
.page=${this.adminPage}
|
|
|
|
|
.user=${this.adminUser}
|
|
|
|
|
.orgs=${this.adminOrgs}
|
|
|
|
|
.selectedOrgId=${this.selectedOrgId}
|
|
|
|
|
.globalAdmin=${this.globalAdmin}
|
|
|
|
|
.dataLoading=${this.dataLoading}
|
|
|
|
|
.dataError=${this.dataError}
|
|
|
|
|
.sessions=${this.sessions}
|
|
|
|
|
.activities=${this.activities}
|
|
|
|
|
.orgMembers=${this.orgMembers}
|
|
|
|
|
.orgInvitations=${this.orgInvitations}
|
|
|
|
|
.orgRoleDefinitions=${this.orgRoleDefinitions}
|
|
|
|
|
.orgApps=${this.orgApps}
|
|
|
|
|
.adminApps=${this.adminApps}
|
|
|
|
|
.passportDevices=${this.passportDevices}
|
|
|
|
|
.passportEnrollment=${this.passportEnrollment}
|
|
|
|
|
.credentialMessage=${this.credentialMessage}
|
|
|
|
|
@idp-admin-navigate=${this.handleAdminNavigate}
|
|
|
|
|
@idp-admin-org-select=${this.handleOrgSelect}
|
|
|
|
|
@idp-admin-org-create=${this.handleOrgCreate}
|
|
|
|
|
@idp-admin-org-update=${this.handleOrgUpdate}
|
|
|
|
|
@idp-admin-org-transfer=${this.handleOrgTransfer}
|
|
|
|
|
@idp-admin-org-delete=${this.handleOrgDelete}
|
|
|
|
|
@idp-admin-session-revoke=${this.handleSessionRevoke}
|
|
|
|
|
@idp-admin-app-toggle=${this.handleAppToggle}
|
|
|
|
|
@idp-admin-password-change=${this.handlePasswordChange}
|
|
|
|
|
@idp-admin-passport-enroll=${this.handlePassportEnroll}
|
|
|
|
|
@idp-admin-passport-revoke=${this.handlePassportRevoke}
|
|
|
|
|
@idp-admin-member-invite=${this.handleMemberInvite}
|
|
|
|
|
@idp-admin-member-remove=${this.handleMemberRemove}
|
|
|
|
|
@idp-admin-member-roles-update=${this.handleMemberRolesUpdate}
|
|
|
|
|
@idp-admin-invitation-resend=${this.handleInvitationResend}
|
|
|
|
|
@idp-admin-invitation-cancel=${this.handleInvitationCancel}
|
|
|
|
|
@idp-admin-org-role-upsert=${this.handleOrgRoleUpsert}
|
|
|
|
|
@idp-admin-org-role-delete=${this.handleOrgRoleDelete}
|
|
|
|
|
@idp-admin-app-role-mappings-update=${this.handleAppRoleMappingsUpdate}
|
|
|
|
|
></idp-admin-shell>
|
2024-10-06 23:56:03 +02:00
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 15:35:37 +00:00
|
|
|
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<plugins.idpCatalog.IdpAdminShell['page'], string | null> = {
|
|
|
|
|
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<plugins.idpCatalog.IIdpAdminNavigateEventDetail>) {
|
|
|
|
|
const page = eventArg.detail.page;
|
|
|
|
|
this.setAdminPage(page);
|
|
|
|
|
const path = this.getPathForPage(page);
|
|
|
|
|
if (path !== null) {
|
|
|
|
|
this.pushDashPath(path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async handleOrgSelect(eventArg: CustomEvent<plugins.idpCatalog.IIdpAdminOrgSelectEventDetail>) {
|
|
|
|
|
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<plugins.idpCatalog.IIdpAdminOrgUpdateEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateOrganization>('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<plugins.idpCatalog.IIdpAdminOrgTransferEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_TransferOwnership>('transferOwnership');
|
|
|
|
|
const response = await request.fire({
|
|
|
|
|
jwt: await idpState.idpClient.getJwt(),
|
|
|
|
|
organizationId: eventArg.detail.organizationId,
|
|
|
|
|
newOwnerId: eventArg.detail.newOwnerId,
|
|
|
|
|
confirmationText: eventArg.detail.confirmationText,
|
2025-12-01 18:56:16 +00:00
|
|
|
});
|
2026-05-07 15:35:37 +00:00
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.message || 'Ownership transfer failed.');
|
2025-12-01 20:03:34 +00:00
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2026-05-07 15:35:37 +00:00
|
|
|
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;
|
2025-12-01 20:03:34 +00:00
|
|
|
}
|
2026-05-07 15:35:37 +00:00
|
|
|
this.applyAccountState();
|
|
|
|
|
this.setAdminPage('org-settings');
|
2025-12-01 18:56:16 +00:00
|
|
|
});
|
2026-05-07 15:35:37 +00:00
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2026-05-07 15:35:37 +00:00
|
|
|
private async handleOrgDelete(eventArg: CustomEvent<plugins.idpCatalog.IIdpAdminOrgDeleteEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteOrganization>('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.');
|
2024-10-06 23:56:03 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-07 15:35:37 +00:00
|
|
|
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<plugins.idpCatalog.IIdpAdminSession[]> {
|
|
|
|
|
const request = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserSessions>('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<plugins.idpCatalog.IIdpAdminActivity[]> {
|
|
|
|
|
const request = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserActivity>('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<plugins.idpCatalog.IIdpAdminMember[]> {
|
|
|
|
|
const currentState = states.accountState.getState();
|
|
|
|
|
const request = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgMembers>('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<plugins.idpCatalog.IIdpAdminInvitation[]> {
|
|
|
|
|
const request = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgInvitations>('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<plugins.idpCatalog.IIdpAdminApp[]> {
|
|
|
|
|
const appsRequest = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetGlobalApps>('getGlobalApps');
|
|
|
|
|
const connectionsRequest = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetAppConnections>('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<plugins.idpCatalog.IIdpAdminApp[]> {
|
|
|
|
|
if (!this.globalAdmin) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const request = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetGlobalAppStats>('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<plugins.idpCatalog.IIdpAdminPassportDevice[]> {
|
|
|
|
|
const request = idpStateArg.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetPassportDevices>('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<void>) {
|
|
|
|
|
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<plugins.idpCatalog.IIdpAdminSessionEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RevokeSession>('revokeSession');
|
|
|
|
|
await request.fire({ jwt: await idpState.idpClient.getJwt(), sessionId: eventArg.detail.sessionId });
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async handleAppToggle(eventArg: CustomEvent<plugins.idpCatalog.IIdpAdminAppToggleEventDetail>) {
|
|
|
|
|
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<plugins.idpInterfaces.request.IReq_ToggleAppConnection>('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<plugins.idpCatalog.IIdpAdminPasswordChangeEventDetail>) {
|
|
|
|
|
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<plugins.idpInterfaces.request.IReq_SetNewPassword>('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<plugins.idpCatalog.IIdpAdminPassportEnrollmentEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CreatePassportEnrollmentChallenge>('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<plugins.idpCatalog.IIdpAdminPassportDeviceEventDetail>) {
|
|
|
|
|
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<plugins.idpInterfaces.request.IReq_RevokePassportDevice>('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<plugins.idpCatalog.IIdpAdminMemberEventDetail>) {
|
|
|
|
|
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<plugins.idpInterfaces.request.IReq_RemoveMember>('removeMember');
|
|
|
|
|
await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, userId: member.userId });
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async handleMemberRolesUpdate(eventArg: CustomEvent<plugins.idpCatalog.IIdpAdminMemberRolesEventDetail>) {
|
|
|
|
|
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<plugins.idpInterfaces.request.IReq_UpdateMemberRoles>('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<plugins.idpCatalog.IIdpAdminOrgRoleUpsertEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpsertOrgRoleDefinition>('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<plugins.idpCatalog.IIdpAdminOrgRoleDeleteEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteOrgRoleDefinition>('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<plugins.idpCatalog.IIdpAdminAppRoleMappingsEventDetail>) {
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateAppRoleMappings>('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<plugins.idpCatalog.IIdpAdminInvitationEventDetail>) {
|
|
|
|
|
const selectedOrg = this.getSelectedOrganization();
|
|
|
|
|
if (!selectedOrg) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.runAdminAction(async () => {
|
|
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
const request = idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ResendInvitation>('resendInvitation');
|
|
|
|
|
await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, invitationId: eventArg.detail.invitationId });
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async handleInvitationCancel(eventArg: CustomEvent<plugins.idpCatalog.IIdpAdminInvitationEventDetail>) {
|
|
|
|
|
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<plugins.idpInterfaces.request.IReq_CancelInvitation>('cancelInvitation');
|
|
|
|
|
await request.fire({ jwt: await idpState.idpClient.getJwt(), organizationId: selectedOrg.id, invitationId: eventArg.detail.invitationId });
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>): Promise<void> {
|
|
|
|
|
super.firstUpdated(_changedProperties);
|
|
|
|
|
await this.domtoolsPromise;
|
|
|
|
|
this.subrouter = this.domtools.router.createSubRouter('/dash');
|
|
|
|
|
await states.accountState.dispatchAction(states.getOrganizationsAction, null);
|
|
|
|
|
this.applyAccountState();
|
2024-10-06 23:56:03 +02:00
|
|
|
|
2024-10-07 15:14:44 +02:00
|
|
|
this.subrouter.on('', async () => {
|
2026-05-07 15:35:37 +00:00
|
|
|
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');
|
2025-12-01 18:56:16 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.subrouter.on('/org/:orgName', async () => {
|
2026-05-07 15:35:37 +00:00
|
|
|
await this.setOrgPage('org-general');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.subrouter.on('/org/:orgName/settings', async () => {
|
|
|
|
|
await this.setOrgPage('org-settings');
|
2025-12-01 04:44:47 +00:00
|
|
|
});
|
|
|
|
|
|
2025-12-01 09:18:48 +00:00
|
|
|
this.subrouter.on('/org/:orgName/apps', async () => {
|
2026-05-07 15:35:37 +00:00
|
|
|
await this.setOrgPage('org-apps');
|
2025-12-01 09:18:48 +00:00
|
|
|
});
|
|
|
|
|
|
2025-12-04 17:45:40 +00:00
|
|
|
this.subrouter.on('/org/:orgName/users', async () => {
|
2026-05-07 15:35:37 +00:00
|
|
|
await this.setOrgPage('org-members');
|
2025-12-04 17:45:40 +00:00
|
|
|
});
|
|
|
|
|
|
2025-12-01 09:44:37 +00:00
|
|
|
this.subrouter.on('/admin', async () => {
|
2026-05-07 15:35:37 +00:00
|
|
|
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');
|
2025-12-01 09:44:37 +00:00
|
|
|
});
|
|
|
|
|
|
2024-10-06 23:56:03 +02:00
|
|
|
this.subrouter._handleRouteState();
|
2024-10-07 15:14:44 +02:00
|
|
|
|
2026-05-07 15:35:37 +00:00
|
|
|
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());
|
|
|
|
|
|
2024-10-07 15:14:44 +02:00
|
|
|
this.registerGarbageFunction(async () => {
|
|
|
|
|
this.subrouter.destroy();
|
|
|
|
|
})
|
2024-10-06 23:56:03 +02:00
|
|
|
}
|
2024-10-07 15:14:44 +02:00
|
|
|
|
|
|
|
|
|
2024-10-06 23:56:03 +02:00
|
|
|
}
|