import { Component, computed, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import {
ApiClientService,
type ApiErrorBody,
type Permission,
type RoleDto,
} from '@boilerplate/api-client';
import { adminPermissionGroups } from './admin-permissions';
@Component({
standalone: true,
imports: [FormsModule, RouterLink],
template: `
Zurueck zur Rollenliste
@if (error(); as currentError) {
{{ messageFor(currentError) }}
@if (currentError.requestId) {
Request-ID: {{ currentError.requestId }}
}
}
@if (!loading()) {
} @else {
}
`,
styles: [
`
a {
display: inline-flex;
margin-bottom: 16px;
color: var(--color-primary-hover);
}
.notice,
.editor {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 8px;
padding: 16px;
}
.notice.error {
border-color: var(--color-danger);
color: var(--color-danger);
margin-bottom: 16px;
}
.editor,
section,
.groups,
fieldset,
.actions {
display: grid;
gap: 14px;
}
h2,
h3 {
margin: 0;
}
label {
display: grid;
gap: 6px;
}
input,
textarea,
button {
min-height: 44px;
}
input,
textarea {
border: 1px solid var(--color-border);
border-radius: 6px;
padding: 8px 10px;
}
textarea {
min-height: 96px;
resize: vertical;
}
button {
border: 0;
border-radius: 6px;
background: var(--color-primary);
color: var(--color-surface);
padding: 0 14px;
}
button.danger {
background: var(--color-danger);
}
button:disabled {
opacity: 0.55;
}
fieldset {
border: 1px solid var(--color-border);
border-radius: 8px;
padding: 12px;
}
fieldset label {
grid-template-columns: 24px 1fr;
align-items: start;
}
code {
display: block;
margin-bottom: 2px;
}
.system {
width: fit-content;
border: 1px solid var(--color-border);
border-radius: 999px;
padding: 4px 8px;
color: var(--color-text-secondary);
}
@media (min-width: 900px) {
.groups {
grid-template-columns: 1fr 1fr;
}
.actions {
grid-template-columns: auto auto;
justify-content: start;
}
}
`,
],
})
export class AdminRoleDetailPageComponent {
private readonly api = inject(ApiClientService);
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
readonly role = signal(null);
readonly loading = signal(false);
readonly error = signal(null);
readonly selectedPermissions = signal(new Set());
readonly groups = adminPermissionGroups;
readonly isNew = computed(() => this.route.snapshot.paramMap.get('id') === 'new');
name = '';
description = '';
constructor() {
if (!this.isNew()) this.load();
}
load(): void {
const id = this.route.snapshot.paramMap.get('id');
if (!id) return;
this.loading.set(true);
this.error.set(null);
this.api.adminRole(id).subscribe({
next: (role) => {
this.role.set(role);
this.name = role.name;
this.description = role.description;
this.selectedPermissions.set(new Set(role.permissions.map((permission) => permission.id)));
this.loading.set(false);
},
error: (error: { error?: ApiErrorBody }) => {
this.error.set(error.error ?? this.genericError());
this.loading.set(false);
},
});
}
toggle(permission: Permission): void {
if (this.role()?.name === 'admin') return;
const next = new Set(this.selectedPermissions());
if (next.has(permission)) next.delete(permission);
else next.add(permission);
this.selectedPermissions.set(next);
}
save(): void {
const body = {
name: this.name,
description: this.description,
permissions: Array.from(this.selectedPermissions()),
};
const request = this.isNew()
? this.api.createAdminRole(body)
: this.api.updateAdminRole(this.role()?.id ?? '', body);
request.subscribe({
next: (role) => {
void this.router.navigate(['/admin/roles', role.id]);
},
error: (error: { error?: ApiErrorBody }) =>
this.error.set(error.error ?? this.genericError()),
});
}
delete(role: RoleDto): void {
if (!confirm('Rolle wirklich loeschen?')) return;
this.api.deleteAdminRole(role.id).subscribe({
next: () => {
void this.router.navigate(['/admin/roles']);
},
error: (error: { error?: ApiErrorBody }) =>
this.error.set(error.error ?? this.genericError()),
});
}
messageFor(error: ApiErrorBody): string {
if (error.code === 'ROLE_STILL_ASSIGNED') return 'Diese Rolle ist noch Benutzern zugewiesen.';
if (error.code === 'SYSTEM_ROLE_PROTECTED') return 'Diese Systemrolle ist geschuetzt.';
if (error.code === 'LAST_ACTIVE_ADMIN_REQUIRED') {
return 'Mindestens ein aktiver Administrator muss erhalten bleiben.';
}
return error.message;
}
private genericError(): ApiErrorBody {
return {
status: 0,
code: 'UNKNOWN',
message: 'Die Rolle konnte nicht verarbeitet werden.',
requestId: '',
};
}
}