feat(opsserver-access): add admin user listing to the access dashboard
This commit is contained in:
@@ -1 +1,2 @@
|
||||
export * from './ops-view-apitokens.js';
|
||||
export * from './ops-view-users.js';
|
||||
|
||||
@@ -100,7 +100,7 @@ export class OpsViewApiTokens extends DeesElement {
|
||||
const { apiTokens } = this.routeState;
|
||||
|
||||
return html`
|
||||
<dees-heading level="2">API Tokens</dees-heading>
|
||||
<dees-heading level="hr">API Tokens</dees-heading>
|
||||
|
||||
<div class="apiTokensContainer">
|
||||
<dees-table
|
||||
|
||||
140
ts_web/elements/access/ops-view-users.ts
Normal file
140
ts_web/elements/access/ops-view-users.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import * as appstate from '../../appstate.js';
|
||||
import { viewHostCss } from '../shared/css.js';
|
||||
|
||||
import {
|
||||
DeesElement,
|
||||
css,
|
||||
cssManager,
|
||||
customElement,
|
||||
html,
|
||||
state,
|
||||
type TemplateResult,
|
||||
} from '@design.estate/dees-element';
|
||||
|
||||
@customElement('ops-view-users')
|
||||
export class OpsViewUsers extends DeesElement {
|
||||
@state() accessor usersState: appstate.IUsersState = {
|
||||
users: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastUpdated: 0,
|
||||
};
|
||||
|
||||
@state() accessor loginState: appstate.ILoginState = {
|
||||
identity: null,
|
||||
isLoggedIn: false,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const usersSub = appstate.usersStatePart
|
||||
.select((s) => s)
|
||||
.subscribe((usersState) => {
|
||||
this.usersState = usersState;
|
||||
});
|
||||
this.rxSubscriptions.push(usersSub);
|
||||
|
||||
const loginSub = appstate.loginStatePart
|
||||
.select((s) => s)
|
||||
.subscribe((loginState) => {
|
||||
this.loginState = loginState;
|
||||
// Re-fetch users when user logs in (fixes race condition where
|
||||
// the view is created before authentication completes)
|
||||
if (loginState.isLoggedIn) {
|
||||
appstate.usersStatePart.dispatchAction(appstate.fetchUsersAction, null);
|
||||
}
|
||||
});
|
||||
this.rxSubscriptions.push(loginSub);
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
cssManager.defaultStyles,
|
||||
viewHostCss,
|
||||
css`
|
||||
.usersContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.roleBadge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.roleBadge.admin {
|
||||
background: ${cssManager.bdTheme('#fef3c7', '#451a03')};
|
||||
color: ${cssManager.bdTheme('#92400e', '#fbbf24')};
|
||||
}
|
||||
|
||||
.roleBadge.user {
|
||||
background: ${cssManager.bdTheme('#e0f2fe', '#0c4a6e')};
|
||||
color: ${cssManager.bdTheme('#075985', '#7dd3fc')};
|
||||
}
|
||||
|
||||
.sessionBadge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
background: ${cssManager.bdTheme('#dcfce7', '#14532d')};
|
||||
color: ${cssManager.bdTheme('#166534', '#4ade80')};
|
||||
}
|
||||
|
||||
.userIdCell {
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
|
||||
font-size: 11px;
|
||||
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
const { users } = this.usersState;
|
||||
const currentUserId = this.loginState.identity?.userId;
|
||||
|
||||
return html`
|
||||
<dees-heading level="2">Users</dees-heading>
|
||||
|
||||
<div class="usersContainer">
|
||||
<dees-table
|
||||
.heading1=${'Users'}
|
||||
.heading2=${'OpsServer user accounts'}
|
||||
.data=${users}
|
||||
.dataName=${'user'}
|
||||
.searchable=${true}
|
||||
.showColumnFilters=${true}
|
||||
.displayFunction=${(user: appstate.IUser) => ({
|
||||
ID: html`<span class="userIdCell">${user.id}</span>`,
|
||||
Username: user.username,
|
||||
Role: this.renderRoleBadge(user.role),
|
||||
Session: user.id === currentUserId
|
||||
? html`<span class="sessionBadge">current</span>`
|
||||
: '',
|
||||
})}
|
||||
></dees-table>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderRoleBadge(role: string): TemplateResult {
|
||||
const cls = role === 'admin' ? 'admin' : 'user';
|
||||
return html`<span class="roleBadge ${cls}">${role}</span>`;
|
||||
}
|
||||
|
||||
async firstUpdated() {
|
||||
if (this.loginState.isLoggedIn) {
|
||||
await appstate.usersStatePart.dispatchAction(appstate.fetchUsersAction, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user