Browse Source

feat(admin-ui): Create admin detail route & create / update admins

Michael Bromley 7 years ago
parent
commit
d611a06e58
20 changed files with 447 additions and 59 deletions
  1. 11 2
      admin-ui/src/app/administrator/administrator.module.ts
  2. 20 2
      admin-ui/src/app/administrator/administrator.routes.ts
  3. 47 0
      admin-ui/src/app/administrator/components/admin-detail/admin-detail.component.html
  4. 0 0
      admin-ui/src/app/administrator/components/admin-detail/admin-detail.component.scss
  5. 157 0
      admin-ui/src/app/administrator/components/admin-detail/admin-detail.component.ts
  6. 87 0
      admin-ui/src/app/administrator/components/permission-grid/permission-grid.component.html
  7. 0 0
      admin-ui/src/app/administrator/components/permission-grid/permission-grid.component.scss
  8. 21 0
      admin-ui/src/app/administrator/components/permission-grid/permission-grid.component.ts
  9. 4 40
      admin-ui/src/app/administrator/components/role-detail/role-detail.component.html
  10. 5 5
      admin-ui/src/app/administrator/components/role-detail/role-detail.component.ts
  11. 22 0
      admin-ui/src/app/administrator/providers/routing/administrator-resolver.ts
  12. 0 4
      admin-ui/src/app/administrator/providers/routing/role-resolver.ts
  13. 5 2
      admin-ui/src/app/common/base-entity-resolver.ts
  14. 18 0
      admin-ui/src/app/data/definitions/administrator-definitions.ts
  15. 31 1
      admin-ui/src/app/data/providers/administrator-data.service.ts
  16. 3 0
      admin-ui/src/app/data/providers/data.service.mock.ts
  17. 2 1
      admin-ui/src/app/shared/components/select-toggle/select-toggle.component.html
  18. 10 1
      admin-ui/src/app/shared/components/select-toggle/select-toggle.component.scss
  19. 1 0
      admin-ui/src/app/shared/components/select-toggle/select-toggle.component.ts
  20. 3 1
      admin-ui/src/i18n-messages/en.json

+ 11 - 2
admin-ui/src/app/administrator/administrator.module.ts

@@ -4,14 +4,23 @@ import { RouterModule } from '@angular/router';
 import { SharedModule } from '../shared/shared.module';
 
 import { administratorRoutes } from './administrator.routes';
+import { AdminDetailComponent } from './components/admin-detail/admin-detail.component';
 import { AdministratorListComponent } from './components/administrator-list/administrator-list.component';
+import { PermissionGridComponent } from './components/permission-grid/permission-grid.component';
 import { RoleDetailComponent } from './components/role-detail/role-detail.component';
 import { RoleListComponent } from './components/role-list/role-list.component';
+import { AdministratorResolver } from './providers/routing/administrator-resolver';
 import { RoleResolver } from './providers/routing/role-resolver';
 
 @NgModule({
     imports: [SharedModule, RouterModule.forChild(administratorRoutes)],
-    declarations: [AdministratorListComponent, RoleListComponent, RoleDetailComponent],
-    providers: [RoleResolver],
+    declarations: [
+        AdministratorListComponent,
+        RoleListComponent,
+        RoleDetailComponent,
+        AdminDetailComponent,
+        PermissionGridComponent,
+    ],
+    providers: [AdministratorResolver, RoleResolver],
 })
 export class AdministratorModule {}

+ 20 - 2
admin-ui/src/app/administrator/administrator.routes.ts

@@ -1,13 +1,15 @@
 import { Route } from '@angular/router';
-import { Role } from 'shared/generated-types';
+import { Administrator, Role } from 'shared/generated-types';
 
 import { createResolveData } from '../common/base-entity-resolver';
 import { detailBreadcrumb } from '../common/detail-breadcrumb';
 import { _ } from '../core/providers/i18n/mark-for-extraction';
 
+import { AdminDetailComponent } from './components/admin-detail/admin-detail.component';
 import { AdministratorListComponent } from './components/administrator-list/administrator-list.component';
 import { RoleDetailComponent } from './components/role-detail/role-detail.component';
 import { RoleListComponent } from './components/role-list/role-list.component';
+import { AdministratorResolver } from './providers/routing/administrator-resolver';
 import { RoleResolver } from './providers/routing/role-resolver';
 
 export const administratorRoutes: Route[] = [
@@ -18,6 +20,12 @@ export const administratorRoutes: Route[] = [
             breadcrumb: _('breadcrumb.administrators'),
         },
     },
+    {
+        path: 'administrators/:id',
+        component: AdminDetailComponent,
+        resolve: createResolveData(AdministratorResolver),
+        data: { breadcrumb: administratorBreadcrumb },
+    },
     {
         path: 'roles',
         component: RoleListComponent,
@@ -33,12 +41,22 @@ export const administratorRoutes: Route[] = [
     },
 ];
 
+export function administratorBreadcrumb(data: any, params: any) {
+    return detailBreadcrumb<Administrator>({
+        entity: data.entity,
+        id: params.id,
+        breadcrumbKey: 'breadcrumb.administrators',
+        getName: admin => `${admin.firstName} ${admin.lastName}`,
+        route: 'administrators',
+    });
+}
+
 export function roleBreadcrumb(data: any, params: any) {
     return detailBreadcrumb<Role>({
         entity: data.entity,
         id: params.id,
         breadcrumbKey: 'breadcrumb.roles',
-        getName: product => product.description,
+        getName: role => role.description,
         route: 'roles',
     });
 }

+ 47 - 0
admin-ui/src/app/administrator/components/admin-detail/admin-detail.component.html

@@ -0,0 +1,47 @@
+<vdr-action-bar>
+    <vdr-ab-left></vdr-ab-left>
+    <vdr-ab-right>
+        <button class="btn btn-primary"
+                *ngIf="isNew$ | async; else updateButton"
+                (click)="create()"
+                [disabled]="administratorForm.invalid || administratorForm.pristine">{{ 'common.create' | translate }}</button>
+        <ng-template #updateButton>
+            <button class="btn btn-primary"
+                    (click)="save()"
+                    [disabled]="(administratorForm.invalid || administratorForm.pristine) && !permissionsChanged">{{ 'common.update' | translate }}</button>
+        </ng-template>
+    </vdr-ab-right>
+</vdr-action-bar>
+
+<form class="form" [formGroup]="administratorForm" >
+    <section class="form-block">
+        <label>{{ 'admin.administrator' | translate }}</label>
+        <vdr-form-field [label]="'admin.email-address' | translate" for="emailAddress">
+            <input id="emailAddress" type="text" formControlName="emailAddress">
+        </vdr-form-field>
+        <vdr-form-field [label]="'admin.first-name' | translate" for="firstName">
+            <input id="firstName" type="text" formControlName="firstName">
+        </vdr-form-field>
+        <vdr-form-field [label]="'admin.last-name' | translate" for="lastName">
+            <input id="lastName" type="text" formControlName="lastName">
+        </vdr-form-field>
+        <vdr-form-field *ngIf="isNew$ | async" [label]="'admin.password' | translate" for="password">
+            <input id="password" type="password" formControlName="password">
+        </vdr-form-field>
+        <vdr-form-field *ngIf="!(isNew$ | async)" [label]="'admin.password' | translate" for="password" [readOnlyToggle]="true">
+            <input id="password" type="password" formControlName="password">
+        </vdr-form-field>
+    </section>
+    <section class="form-block">
+        <label>{{ 'admin.roles' | translate }}</label>
+        <ng-select [items]="allRoles$ | async"
+                   [multiple]="true"
+                   [hideSelected]="true"
+                   formControlName="roles"
+                   (change)="rolesChanged($event)"
+                   bindLabel="description"></ng-select>
+    </section>
+
+    <vdr-permission-grid [permissions]="selectedRolePermissions"
+                         [readonly]="true"></vdr-permission-grid>
+</form>

+ 0 - 0
admin-ui/src/app/administrator/components/admin-detail/admin-detail.component.scss


+ 157 - 0
admin-ui/src/app/administrator/components/admin-detail/admin-detail.component.ts

@@ -0,0 +1,157 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Observable } from 'rxjs';
+import { mergeMap, take } from 'rxjs/operators';
+import {
+    Administrator,
+    CreateAdministratorInput,
+    LanguageCode,
+    Permission,
+    Role,
+    UpdateAdministratorInput,
+} from 'shared/generated-types';
+
+import { BaseDetailComponent } from '../../../common/base-detail.component';
+import { _ } from '../../../core/providers/i18n/mark-for-extraction';
+import { NotificationService } from '../../../core/providers/notification/notification.service';
+import { DataService } from '../../../data/providers/data.service';
+
+@Component({
+    selector: 'vdr-admin-detail',
+    templateUrl: './admin-detail.component.html',
+    styleUrls: ['./admin-detail.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AdminDetailComponent extends BaseDetailComponent<Administrator> implements OnInit, OnDestroy {
+    administrator$: Observable<Administrator>;
+    allRoles$: Observable<Role[]>;
+    selectedRoles: Role[] = [];
+    administratorForm: FormGroup;
+    selectedRolePermissions: { [K in Permission]: boolean } = {} as any;
+
+    constructor(
+        router: Router,
+        route: ActivatedRoute,
+        private changeDetector: ChangeDetectorRef,
+        private dataService: DataService,
+        private formBuilder: FormBuilder,
+        private notificationService: NotificationService,
+    ) {
+        super(route, router);
+        this.administratorForm = this.formBuilder.group({
+            emailAddress: ['', Validators.required],
+            firstName: ['', Validators.required],
+            lastName: ['', Validators.required],
+            password: [''],
+            roles: [[]],
+        });
+    }
+
+    ngOnInit() {
+        this.init();
+        this.administrator$ = this.entity$;
+        this.allRoles$ = this.dataService.administrator.getRoles(99999).mapStream(item => item.roles.items);
+    }
+
+    ngOnDestroy(): void {
+        this.destroy();
+    }
+
+    rolesChanged(roles: Role[]) {
+        this.buildPermissionsMap();
+    }
+
+    create() {
+        const formValue = this.administratorForm.value;
+        const administrator: CreateAdministratorInput = {
+            emailAddress: formValue.emailAddress,
+            firstName: formValue.firstName,
+            lastName: formValue.lastName,
+            password: formValue.password,
+            roleIds: formValue.roles.map(role => role.id),
+        };
+        this.dataService.administrator.createAdministrator(administrator).subscribe(
+            data => {
+                this.notificationService.success(_('common.notify-create-success'), {
+                    entity: 'Administrator',
+                });
+                this.administratorForm.markAsPristine();
+                this.changeDetector.markForCheck();
+                this.router.navigate(['../', data.createAdministrator.id], { relativeTo: this.route });
+            },
+            err => {
+                this.notificationService.error(_('common.notify-create-error'), {
+                    entity: 'Administrator',
+                    error: err.message,
+                });
+            },
+        );
+    }
+
+    save() {
+        this.administrator$
+            .pipe(
+                take(1),
+                mergeMap(({ id }) => {
+                    const formValue = this.administratorForm.value;
+                    const administrator: UpdateAdministratorInput = {
+                        id,
+                        emailAddress: formValue.emailAddress,
+                        firstName: formValue.firstName,
+                        lastName: formValue.lastName,
+                        password: formValue.password,
+                        roleIds: formValue.roles.map(role => role.id),
+                    };
+                    return this.dataService.administrator.updateAdministrator(administrator);
+                }),
+            )
+            .subscribe(
+                data => {
+                    this.notificationService.success(_('common.notify-update-success'), {
+                        entity: 'Administrator',
+                    });
+                    this.administratorForm.markAsPristine();
+                    this.changeDetector.markForCheck();
+                },
+                err => {
+                    this.notificationService.error(_('common.notify-update-error'), {
+                        entity: 'Administrator',
+                        error: err.message,
+                    });
+                },
+            );
+    }
+
+    protected setFormValues(administrator: Administrator, languageCode: LanguageCode): void {
+        this.administratorForm.patchValue({
+            emailAddress: administrator.emailAddress,
+            firstName: administrator.firstName,
+            lastName: administrator.lastName,
+            roles: administrator.user.roles,
+        });
+        const passwordControl = this.administratorForm.get('password');
+        if (passwordControl) {
+            if (!administrator.id) {
+                passwordControl.setValidators([Validators.required]);
+            } else {
+                passwordControl.setValidators([]);
+            }
+        }
+        this.buildPermissionsMap();
+    }
+
+    private buildPermissionsMap() {
+        const permissionsControl = this.administratorForm.get('roles');
+        if (permissionsControl) {
+            const permissions = permissionsControl.value.reduce(
+                (output, role: Role) => [...output, ...role.permissions],
+                [],
+            );
+            this.selectedRolePermissions = {} as any;
+            for (const permission of Object.keys(Permission)) {
+                this.selectedRolePermissions[permission] = permissions.includes(permission);
+            }
+        }
+    }
+}

+ 87 - 0
admin-ui/src/app/administrator/components/permission-grid/permission-grid.component.html

@@ -0,0 +1,87 @@
+<table class="table">
+    <tr>
+        <th>{{ 'admin.section' | translate }}</th>
+        <th>{{ 'admin.create' | translate }}</th>
+        <th>{{ 'admin.read' | translate }}</th>
+        <th>{{ 'admin.update' | translate }}</th>
+        <th>{{ 'admin.delete' | translate }}</th>
+    </tr>
+    <tbody>
+    <tr>
+        <td>{{ 'admin.catalog' | translate }}</td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['CreateCatalog']"
+                               (selectedChange)="setPermission('CreateCatalog', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['ReadCatalog']"
+                               (selectedChange)="setPermission('ReadCatalog', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['UpdateCatalog']"
+                               (selectedChange)="setPermission('UpdateCatalog', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['DeleteCatalog']"
+                               (selectedChange)="setPermission('DeleteCatalog', $event)"></vdr-select-toggle></td>
+    </tr>
+    <tr>
+        <td>{{ 'admin.customer' | translate }}</td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['CreateCustomer']"
+                               (selectedChange)="setPermission('CreateCustomer', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['ReadCustomer']"
+                               (selectedChange)="setPermission('ReadCustomer', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['UpdateCustomer']"
+                               (selectedChange)="setPermission('UpdateCustomer', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['DeleteCustomer']"
+                               (selectedChange)="setPermission('DeleteCustomer', $event)"></vdr-select-toggle></td>
+    </tr>
+    <tr>
+        <td>{{ 'admin.order' | translate }}</td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['CreateOrder']"
+                               (selectedChange)="setPermission('CreateOrder', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['ReadOrder']"
+                               (selectedChange)="setPermission('ReadOrder', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['UpdateOrder']"
+                               (selectedChange)="setPermission('UpdateOrder', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['DeleteOrder']"
+                               (selectedChange)="setPermission('DeleteOrder', $event)"></vdr-select-toggle></td>
+    </tr>
+    <tr>
+        <td>{{ 'admin.administrator' | translate }}</td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['CreateAdministrator']"
+                               (selectedChange)="setPermission('CreateAdministrator', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['ReadAdministrator']"
+                               (selectedChange)="setPermission('ReadAdministrator', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['UpdateAdministrator']"
+                               (selectedChange)="setPermission('UpdateAdministrator', $event)"></vdr-select-toggle></td>
+        <td><vdr-select-toggle size="small"
+                               [disabled]="readonly"
+                               [selected]="permissions['DeleteAdministrator']"
+                               (selectedChange)="setPermission('DeleteAdministrator', $event)"></vdr-select-toggle></td>
+    </tr>
+    </tbody>
+</table>

+ 0 - 0
admin-ui/src/app/administrator/components/permission-grid/permission-grid.component.scss


+ 21 - 0
admin-ui/src/app/administrator/components/permission-grid/permission-grid.component.ts

@@ -0,0 +1,21 @@
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
+import { Permission } from 'shared/generated-types';
+
+/**
+ * A table showing and allowing the setting of all possible CRUD permissions.
+ */
+@Component({
+    selector: 'vdr-permission-grid',
+    templateUrl: './permission-grid.component.html',
+    styleUrls: ['./permission-grid.component.scss'],
+    changeDetection: ChangeDetectionStrategy.Default,
+})
+export class PermissionGridComponent {
+    @Input() permissions: { [K in Permission]: boolean } = {} as any;
+    @Input() readonly = false;
+    @Output() permissionChange = new EventEmitter<{ permission: string; value: boolean }>();
+
+    setPermission(permission: string, value: boolean) {
+        this.permissionChange.emit({ permission, value });
+    }
+}

+ 4 - 40
admin-ui/src/app/administrator/components/role-detail/role-detail.component.html

@@ -23,44 +23,8 @@
             <input id="code" type="text" formControlName="code">
         </vdr-form-field>
     </section>
-
-    <table class="table">
-        <tr>
-            <th>{{ 'admin.section' | translate }}</th>
-            <th>{{ 'admin.create' | translate }}</th>
-            <th>{{ 'admin.read' | translate }}</th>
-            <th>{{ 'admin.update' | translate }}</th>
-            <th>{{ 'admin.delete' | translate }}</th>
-        </tr>
-        <tbody>
-        <tr>
-            <td>{{ 'admin.catalog' | translate }}</td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['CreateCatalog']" (selectedChange)="setPermission('CreateCatalog', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['ReadCatalog']" (selectedChange)="setPermission('ReadCatalog', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['UpdateCatalog']" (selectedChange)="setPermission('UpdateCatalog', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['DeleteCatalog']" (selectedChange)="setPermission('DeleteCatalog', $event)"></vdr-select-toggle></td>
-        </tr>
-        <tr>
-            <td>{{ 'admin.customer' | translate }}</td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['CreateCustomer']" (selectedChange)="setPermission('CreateCustomer', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['ReadCustomer']" (selectedChange)="setPermission('ReadCustomer', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['UpdateCustomer']" (selectedChange)="setPermission('UpdateCustomer', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['DeleteCustomer']" (selectedChange)="setPermission('DeleteCustomer', $event)"></vdr-select-toggle></td>
-        </tr>
-        <tr>
-            <td>{{ 'admin.order' | translate }}</td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['CreateOrder']" (selectedChange)="setPermission('CreateOrder', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['ReadOrder']" (selectedChange)="setPermission('ReadOrder', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['UpdateOrder']" (selectedChange)="setPermission('UpdateOrder', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['DeleteOrder']" (selectedChange)="setPermission('DeleteOrder', $event)"></vdr-select-toggle></td>
-        </tr>
-        <tr>
-            <td>{{ 'admin.administrator' | translate }}</td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['CreateAdministrator']" (selectedChange)="setPermission('CreateAdministrator', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['ReadAdministrator']" (selectedChange)="setPermission('ReadAdministrator', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['UpdateAdministrator']" (selectedChange)="setPermission('UpdateAdministrator', $event)"></vdr-select-toggle></td>
-            <td><vdr-select-toggle size="small" [selected]="permissions['DeleteAdministrator']" (selectedChange)="setPermission('DeleteAdministrator', $event)"></vdr-select-toggle></td>
-        </tr>
-        </tbody>
-    </table>
+    <section class="form-block">
+        <label>{{ 'admin.permissions' | translate }}</label>
+        <vdr-permission-grid [permissions]="permissions" (permissionChange)="setPermission($event)"></vdr-permission-grid>
+    </section>
 </form>

+ 5 - 5
admin-ui/src/app/administrator/components/role-detail/role-detail.component.ts

@@ -1,10 +1,9 @@
-import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { ActivatedRoute, Router } from '@angular/router';
 import { Observable } from 'rxjs';
 import { mergeMap, take } from 'rxjs/operators';
-import { CreateRoleInput, LanguageCode, Role, UpdateRoleInput } from 'shared/generated-types';
-import { Permission } from 'shared/generated-types';
+import { CreateRoleInput, LanguageCode, Permission, Role, UpdateRoleInput } from 'shared/generated-types';
 
 import { BaseDetailComponent } from '../../../common/base-detail.component';
 import { normalizeString } from '../../../common/utilities/normalize-string';
@@ -16,6 +15,7 @@ import { DataService } from '../../../data/providers/data.service';
     selector: 'vdr-role-detail',
     templateUrl: './role-detail.component.html',
     styleUrls: ['./role-detail.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class RoleDetailComponent extends BaseDetailComponent<Role> implements OnInit, OnDestroy {
     role$: Observable<Role>;
@@ -57,8 +57,8 @@ export class RoleDetailComponent extends BaseDetailComponent<Role> implements On
         }
     }
 
-    setPermission(key: string, value: boolean) {
-        this.permissions[key] = value;
+    setPermission(change: { permission: string; value: boolean }) {
+        this.permissions = { ...this.permissions, [change.permission]: change.value };
         this.permissionsChanged = true;
     }
 

+ 22 - 0
admin-ui/src/app/administrator/providers/routing/administrator-resolver.ts

@@ -0,0 +1,22 @@
+import { Injectable } from '@angular/core';
+import { Administrator, Role } from 'shared/generated-types';
+
+import { BaseEntityResolver } from '../../../common/base-entity-resolver';
+import { DataService } from '../../../data/providers/data.service';
+
+@Injectable()
+export class AdministratorResolver extends BaseEntityResolver<Administrator> {
+    constructor(private dataService: DataService) {
+        super(
+            {
+                __typename: 'Administrator' as 'Administrator',
+                id: '',
+                emailAddress: '',
+                firstName: '',
+                lastName: '',
+                user: { roles: [] } as any,
+            },
+            id => this.dataService.administrator.getAdministrator(id).mapStream(data => data.administrator),
+        );
+    }
+}

+ 0 - 4
admin-ui/src/app/administrator/providers/routing/role-resolver.ts

@@ -4,10 +4,6 @@ import { Role } from 'shared/generated-types';
 import { BaseEntityResolver } from '../../../common/base-entity-resolver';
 import { DataService } from '../../../data/providers/data.service';
 
-/**
- * Resolves the id from the path into a Customer entity.
- */
-
 @Injectable()
 export class RoleResolver extends BaseEntityResolver<Role> {
     constructor(private dataService: DataService) {

+ 5 - 2
admin-ui/src/app/common/base-entity-resolver.ts

@@ -1,6 +1,6 @@
 import { ActivatedRouteSnapshot, Resolve, ResolveData, RouterStateSnapshot } from '@angular/router';
 import { Observable, of } from 'rxjs';
-import { filter, map, take } from 'rxjs/operators';
+import { filter, map, shareReplay, take } from 'rxjs/operators';
 import { Type } from 'shared/shared-types';
 import { notNullOrUndefined } from 'shared/shared-utils';
 
@@ -32,7 +32,10 @@ export class BaseEntityResolver<T> implements Resolve<Observable<T>> {
         if (id === 'create') {
             return of(of(this.emptyEntity));
         } else if (id) {
-            const stream = this.entityStream(id).pipe(filter(notNullOrUndefined));
+            const stream = this.entityStream(id).pipe(
+                filter(notNullOrUndefined),
+                shareReplay(1),
+            );
 
             return stream.pipe(
                 take(1),

+ 18 - 0
admin-ui/src/app/data/definitions/administrator-definitions.ts

@@ -45,6 +45,15 @@ export const GET_ADMINISTRATORS = gql`
     ${ADMINISTRATOR_FRAGMENT}
 `;
 
+export const GET_ADMINISTRATOR = gql`
+    query GetAdministrator($id: ID!) {
+        administrator(id: $id) {
+            ...Administrator
+        }
+    }
+    ${ADMINISTRATOR_FRAGMENT}
+`;
+
 export const CREATE_ADMINISTRATOR = gql`
     mutation CreateAdministrator($input: CreateAdministratorInput!) {
         createAdministrator(input: $input) {
@@ -54,6 +63,15 @@ export const CREATE_ADMINISTRATOR = gql`
     ${ADMINISTRATOR_FRAGMENT}
 `;
 
+export const UPDATE_ADMINISTRATOR = gql`
+    mutation UpdateAdministrator($input: UpdateAdministratorInput!) {
+        updateAdministrator(input: $input) {
+            ...Administrator
+        }
+    }
+    ${ADMINISTRATOR_FRAGMENT}
+`;
+
 export const GET_ROLES = gql`
     query GetRoles($options: RoleListOptions) {
         roles(options: $options) {

+ 31 - 1
admin-ui/src/app/data/providers/administrator-data.service.ts

@@ -1,25 +1,35 @@
 import { Observable } from 'rxjs';
 import {
+    CreateAdministrator,
+    CreateAdministratorInput,
+    CreateAdministratorVariables,
     CreateRole,
     CreateRoleInput,
     CreateRoleVariables,
+    GetAdministrator,
     GetAdministrators,
     GetAdministratorsVariables,
+    GetAdministratorVariables,
     GetRole,
     GetRoles,
     GetRolesVariables,
     GetRoleVariables,
+    UpdateAdministrator,
+    UpdateAdministratorInput,
+    UpdateAdministratorVariables,
     UpdateRole,
     UpdateRoleInput,
     UpdateRoleVariables,
 } from 'shared/generated-types';
 
-import { getDefaultLanguage } from '../../common/utilities/get-default-language';
 import {
+    CREATE_ADMINISTRATOR,
     CREATE_ROLE,
+    GET_ADMINISTRATOR,
     GET_ADMINISTRATORS,
     GET_ROLE,
     GET_ROLES,
+    UPDATE_ADMINISTRATOR,
     UPDATE_ROLE,
 } from '../definitions/administrator-definitions';
 import { QueryResult } from '../query-result';
@@ -41,6 +51,26 @@ export class AdministratorDataService {
         });
     }
 
+    getAdministrator(id: string): QueryResult<GetAdministrator, GetAdministratorVariables> {
+        return this.baseDataService.query<GetAdministrator, GetAdministratorVariables>(GET_ADMINISTRATOR, {
+            id,
+        });
+    }
+
+    createAdministrator(input: CreateAdministratorInput): Observable<CreateAdministrator> {
+        return this.baseDataService.mutate<CreateAdministrator, CreateAdministratorVariables>(
+            CREATE_ADMINISTRATOR,
+            { input },
+        );
+    }
+
+    updateAdministrator(input: UpdateAdministratorInput): Observable<UpdateAdministrator> {
+        return this.baseDataService.mutate<UpdateAdministrator, UpdateAdministratorVariables>(
+            UPDATE_ADMINISTRATOR,
+            { input },
+        );
+    }
+
     getRoles(take: number = 10, skip: number = 0): QueryResult<GetRoles, GetRolesVariables> {
         return this.baseDataService.query<GetRoles, GetRolesVariables>(GET_ROLES, {
             options: {

+ 3 - 0
admin-ui/src/app/data/providers/data.service.mock.ts

@@ -32,6 +32,9 @@ export function spyObservable(name: string, returnValue: any = {}): jasmine.Spy
 export class MockDataService implements DataServiceMock {
     administrator = {
         getAdministrators: spyQueryResult('getAdministrators'),
+        getAdministrator: spyQueryResult('getAdministrator'),
+        createAdministrator: spyObservable('createAdministrator'),
+        updateAdministrator: spyObservable('updateAdministrator'),
         getRoles: spyQueryResult('getRoles'),
         getRole: spyQueryResult('getRole'),
         createRole: spyObservable('createRole'),

+ 2 - 1
admin-ui/src/app/shared/components/select-toggle/select-toggle.component.html

@@ -1,6 +1,7 @@
 <div class="toggle"
+     [class.disabled]="disabled"
      [class.small]="size === 'small'"
-     tabindex="0"
+     [attr.tabindex]="disabled ? null : 0"
      [class.selected]="selected"
      (keydown.enter)="selectedChange.emit(!selected)"
      (keydown.space)="$event.preventDefault(); selectedChange.emit(!selected)"

+ 10 - 1
admin-ui/src/app/shared/components/select-toggle/select-toggle.component.scss

@@ -27,7 +27,7 @@
         height: 24px;
     }
 
-    &:hover {
+    &:not(.disabled):hover {
         border-color: $color-success;
         background-color: transparentize($color-success, 0.9);
     }
@@ -36,10 +36,19 @@
         background-color: $color-success;
         border-color: darken($color-success, 5%);
         color: white;
+
+        &:not(.disabled):hover {
+            background-color: transparentize($color-success, 0.2);
+            border-color: $color-success;
+        }
     }
 
     &:focus {
         outline: none;
         box-shadow: 0 0 2px 2px #6bc1e3
     }
+
+    &.disabled {
+        cursor: default;
+    }
 }

+ 1 - 0
admin-ui/src/app/shared/components/select-toggle/select-toggle.component.ts

@@ -12,5 +12,6 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output
 export class SelectToggleComponent {
     @Input() size: 'small' | 'large' = 'large';
     @Input() selected = false;
+    @Input() disabled = false;
     @Output() selectedChange = new EventEmitter<boolean>();
 }

+ 3 - 1
admin-ui/src/i18n-messages/en.json

@@ -7,14 +7,16 @@
     "create-new-role": "Create new role",
     "customer": "Customer",
     "delete": "Delete",
-    "description": "Delete",
+    "description": "Description",
     "email-address": "Email address",
     "first-name": "First name",
     "last-name": "Last name",
     "order": "Order",
+    "password": "Password",
     "permissions": "Permissions",
     "read": "Read",
     "role": "Role",
+    "roles": "Roles",
     "section": "Section",
     "update": "Update"
   },