change to seperate value-picker component

This commit is contained in:
rubikscraft 2022-03-28 22:12:06 +02:00
parent bf2fa9e771
commit 87af2c47c0
No known key found for this signature in database
GPG key ID: 1463EBE9200A5CD4
20 changed files with 274 additions and 322 deletions

View file

@ -0,0 +1,39 @@
<mat-form-field class="value-picker" appearance="outline" color="accent">
<mat-label>{{ nameCapMul }}</mat-label>
<mat-chip-list #chipList>
<mat-chip
*ngFor="let item of this.myControl.value"
[removable]="!isDisabled(item)"
[disabled]="isDisabled(item)"
(removed)="removeItem(item)"
>
{{ item }}
<button *ngIf="!isDisabled(item)" matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
<input
placeholder="Add {{name}}..."
#fruitInput
[formControl]="inputControl"
[value]="inputControl.value"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="addItemInput($event)"
autocorrect="off"
autocapitalize="none"
/>
</mat-chip-list>
<mat-autocomplete
#auto="matAutocomplete"
(optionSelected)="addItemSelect($event)"
>
<mat-option
*ngFor="let item of selectable | async"
[value]="item"
>
{{ item }}
</mat-option>
</mat-autocomplete>
</mat-form-field>

View file

@ -0,0 +1,7 @@
mat-form-field {
width: 100%;
}
:host {
display: block;
}

View file

@ -0,0 +1,108 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Component, Input, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
import Fuse from 'fuse.js';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { BehaviorSubject } from 'rxjs';
import { Required } from 'src/app/models/decorators/required.decorator';
@Component({
selector: 'values-picker',
templateUrl: './values-picker.component.html',
styleUrls: ['./values-picker.component.scss'],
})
export class ValuesPickerComponent implements OnInit {
// Static data
readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
// Ui niceties
@Input('name') @Required name: string = '';
public get nameCap(): string {
return this.name.charAt(0).toUpperCase() + this.name.slice(1);
}
public get nameCapMul(): string {
return `${this.nameCap}s`;
}
// Inputs/oututs
@Input('selection-list') @Required fullSelection: string[] = [];
@Input('control') @Required myControl: FormControl;
@Input('disabled-list') disabledSelection: string[] = [];
// Selection
private selectableSubject = new BehaviorSubject<string[]>([]);
public selectable = this.selectableSubject.asObservable();
public inputControl = new FormControl('');
public ngOnInit(): void {
this.subscribeInputValue();
this.subscribeMyValue();
}
public isDisabled(value: string): boolean {
return this.disabledSelection.includes(value);
}
// Remove/add
public removeItem(item: string) {
const selected: string[] = this.myControl.value;
const newSelection = selected.filter((s) => s !== item);
this.myControl.setValue(newSelection);
}
public addItemInput(event: MatChipInputEvent) {
const value = (event.value ?? '').trim();
this.addItem(value);
}
public addItemSelect(event: MatAutocompleteSelectedEvent): void {
this.addItem(event.option.viewValue);
}
private addItem(value: string) {
console.log('adding', value);
const selectable = this.selectableSubject.getValue();
if (this.isDisabled(value) || !selectable.includes(value)) return;
const selected: string[] = this.myControl.value;
this.myControl.setValue([...selected, value]);
this.inputControl.setValue('');
}
// Update and subscribe
private updateSelectable() {
const selected: string[] = this.myControl.value;
const available = this.fullSelection.filter(
(s) => !this.isDisabled(s) && !selected.includes(s)
);
const searchValue = this.inputControl.value;
if (searchValue && available.length > 0) {
const fuse = new Fuse(available);
const result = fuse.search(searchValue).map((r) => r.item);
this.selectableSubject.next(result);
} else {
this.selectableSubject.next(available);
}
}
@AutoUnsubscribe()
private subscribeInputValue() {
return this.inputControl.valueChanges.subscribe((value) => {
this.updateSelectable();
});
}
@AutoUnsubscribe()
private subscribeMyValue() {
return this.myControl.valueChanges.subscribe((value) => {
this.updateSelectable();
});
}
}

View file

@ -0,0 +1,25 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { ValuesPickerComponent } from './values-picker.component';
@NgModule({
declarations: [ValuesPickerComponent],
imports: [
CommonModule,
MatIconModule,
MatFormFieldModule,
MatInputModule,
MatChipsModule,
MatAutocompleteModule,
FormsModule,
ReactiveFormsModule,
],
exports: [ValuesPickerComponent],
})
export class ValuesPickerModule {}

View file

@ -0,0 +1,15 @@
export function Required(target: object, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
get() {
throw new Error(`Attribute ${propertyKey} is required`);
},
set(value) {
Object.defineProperty(target, propertyKey, {
value,
writable: true,
configurable: true,
});
},
configurable: true,
});
}

View file

@ -3,13 +3,18 @@ import { Route } from '@angular/router';
export type PRouteData = {
page?: {
// This is not the tab-title, but the title in the sidenav
title?: string;
// This is not the favicon, but the icon in the sidenav
icon?: string;
category?: string;
};
permissions?: string[];
noContainer?: boolean;
sidebar?: ComponentType<unknown>;
// This is not meant to be set by the user, but by a resolver service
// It just cant be stored anywhere else
_sidebar_portal?: Portal<unknown>;
};

View file

@ -20,6 +20,7 @@ export class LoginControl {
return CreatePasswordError(this.password.errors);
}
// This getter firstly verifies the form, RawData does not
public getData(): Failable<UserPassModel> {
if (this.username.errors || this.password.errors)
return Fail('Invalid username or password');

View file

@ -3,7 +3,10 @@ import { Fail, Failable } from 'picsur-shared/dist/types';
import { UserPassModel } from '../forms-dto/userpass.dto';
import { Compare } from '../validators/compare.validator';
import {
CreatePasswordError, CreateUsernameError, PasswordValidators, UsernameValidators
CreatePasswordError,
CreateUsernameError,
PasswordValidators,
UsernameValidators
} from '../validators/user.validator';
export class RegisterControl {
@ -26,6 +29,7 @@ export class RegisterControl {
return CreatePasswordError(this.passwordConfirm.errors);
}
// This getter firstly verifies the form, RawData does not
public getData(): Failable<UserPassModel> {
if (
this.username.errors ||

View file

@ -1,24 +1,11 @@
import { FormControl } from '@angular/forms';
import Fuse from 'fuse.js';
import { BehaviorSubject, Subscription } from 'rxjs';
import { RoleModel } from '../forms-dto/role.dto';
import { RoleNameValidators } from '../validators/role.validator';
import { CreateUsernameError } from '../validators/user.validator';
export class UpdateRoleControl {
// Set once
private permissions: string[] = [];
// Variables
private selectablePermissionsSubject = new BehaviorSubject<string[]>([]);
private permissionsInputSubscription: null | Subscription;
public rolename = new FormControl('', RoleNameValidators);
public permissionControl = new FormControl('', []);
public selectablePermissions =
this.selectablePermissionsSubject.asObservable();
public selectedPermissions: string[] = [];
public permissions = new FormControl([]);
public get rolenameValue() {
return this.rolename.value;
@ -28,48 +15,18 @@ export class UpdateRoleControl {
return CreateUsernameError(this.rolename.errors);
}
constructor() {
this.permissionsInputSubscription =
this.permissionControl.valueChanges.subscribe((roles) => {
this.updateSelectablePermissions();
});
}
public destroy() {
if (this.permissionsInputSubscription) {
this.permissionsInputSubscription.unsubscribe();
this.permissionsInputSubscription = null;
}
}
public addPermission(permission: string) {
if (!this.selectablePermissionsSubject.value.includes(permission)) return;
this.selectedPermissions.push(permission);
this.clearInput();
}
public removePermission(permission: string) {
this.selectedPermissions = this.selectedPermissions.filter(
(r) => r !== permission
);
this.updateSelectablePermissions();
public get selectedPermissions() {
return this.permissions.value;
}
// Data interaction
public putAllPermissions(permissions: string[]) {
this.permissions = permissions;
this.updateSelectablePermissions();
}
public putRoleName(rolename: string) {
this.rolename.setValue(rolename);
}
public putPermissions(permissions: string[]) {
this.selectedPermissions = permissions;
this.updateSelectablePermissions();
this.permissions.setValue(permissions);
}
public getData(): RoleModel {
@ -78,28 +35,4 @@ export class UpdateRoleControl {
permissions: this.selectedPermissions,
};
}
// Logic
private updateSelectablePermissions() {
const availablePermissins = this.permissions.filter(
(r) => !this.selectedPermissions.includes(r)
);
const searchValue = this.permissionControl.value;
if (searchValue && availablePermissins.length > 0) {
const fuse = new Fuse(availablePermissins);
const result = fuse
.search(this.permissionControl.value ?? '')
.map((r) => r.item);
this.selectablePermissionsSubject.next(result);
} else {
this.selectablePermissionsSubject.next(availablePermissins);
}
}
private clearInput() {
this.permissionControl.setValue('');
}
}

View file

@ -1,7 +1,4 @@
import { FormControl } from '@angular/forms';
import Fuse from 'fuse.js';
import { ERole } from 'picsur-shared/dist/entities/role.entity';
import { BehaviorSubject, Subscription } from 'rxjs';
import { FullUserModel } from '../forms-dto/fulluser.dto';
import {
CreatePasswordError,
@ -11,23 +8,9 @@ import {
} from '../validators/user.validator';
export class UpdateUserControl {
// Special roles
private SoulBoundRolesList: string[] = [];
// Set once
private fullRoles: ERole[] = [];
private roles: string[] = [];
// Variables
private selectableRolesSubject = new BehaviorSubject<string[]>([]);
private rolesInputSubscription: null | Subscription;
public username = new FormControl('', UsernameValidators);
public password = new FormControl('', PasswordValidators);
public rolesControl = new FormControl('', []);
public selectableRoles = this.selectableRolesSubject.asObservable();
public selectedRoles: string[] = [];
public roles = new FormControl([]);
public get usernameValue() {
return this.username.value;
@ -41,74 +24,18 @@ export class UpdateUserControl {
return CreatePasswordError(this.password.errors);
}
constructor() {
this.rolesInputSubscription = this.rolesControl.valueChanges.subscribe(
(roles) => {
this.updateSelectableRoles();
}
);
}
public destroy() {
if (this.rolesInputSubscription) {
this.rolesInputSubscription.unsubscribe();
this.rolesInputSubscription = null;
}
}
public addRole(role: string) {
if (!this.selectableRolesSubject.value.includes(role)) return;
this.selectedRoles.push(role);
this.clearInput();
}
public removeRole(role: string) {
this.selectedRoles = this.selectedRoles.filter((r) => r !== role);
this.updateSelectableRoles();
}
public isRemovable(role: string) {
if (this.SoulBoundRolesList.includes(role)) return false;
return true;
}
public getEffectivePermissions(): string[] {
const permissions: string[] = [];
for (const role of this.selectedRoles) {
const fullRole = this.fullRoles.find((r) => r.name === role);
if (!fullRole) {
console.warn(`Role ${role} not found`);
continue;
}
permissions.push(
...fullRole.permissions.filter((p) => !permissions.includes(p))
);
}
return permissions;
public get selectedRoles(): string[] {
return this.roles.value;
}
// Data interaction
public putAllRoles(roles: ERole[]) {
this.fullRoles = roles;
this.roles = roles.map((role) => role.name);
this.updateSelectableRoles();
}
public putUsername(username: string) {
this.username.setValue(username);
}
public putRoles(roles: string[]) {
this.selectedRoles = roles;
this.updateSelectableRoles();
}
public putSoulBoundRoles(roles: string[]) {
this.SoulBoundRolesList = roles;
this.roles.setValue(roles);
}
public getData(): FullUserModel {
@ -118,30 +45,4 @@ export class UpdateUserControl {
roles: this.selectedRoles,
};
}
// Logic
private updateSelectableRoles() {
const availableRoles = this.roles.filter(
// Not available if either already selected, or the role is not addable/removable
(r) =>
!(this.selectedRoles.includes(r) || this.SoulBoundRolesList.includes(r))
);
const searchValue = this.rolesControl.value;
if (searchValue && availableRoles.length > 0) {
const fuse = new Fuse(availableRoles);
const result = fuse
.search(this.rolesControl.value ?? '')
.map((r) => r.item);
this.selectableRolesSubject.next(result);
} else {
this.selectableRolesSubject.next(availableRoles);
}
}
private clearInput() {
this.rolesControl.setValue('');
}
}

View file

@ -6,7 +6,5 @@ import { Component, OnInit } from '@angular/core';
export class SettingsGeneralComponent implements OnInit {
constructor() {}
ngOnInit(): void {
}
ngOnInit(): void {}
}

View file

@ -28,43 +28,11 @@
<div class="row">
<div class="col-lg-6 col-12">
<mat-form-field appearance="outline" color="accent">
<mat-label>Permissions</mat-label>
<mat-chip-list #chipList aria-label="Permissions Selection">
<mat-chip
*ngFor="let permission of model.selectedPermissions"
(removed)="removePermission(permission)"
>
{{ uiFriendlyPermission(permission) }}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
<input
placeholder="Add permission..."
#fruitInput
[formControl]="model.permissionControl"
[value]="model.permissionControl.value"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="addPermission($event)"
autocorrect="off"
autocapitalize="none"
/>
</mat-chip-list>
<mat-autocomplete
#auto="matAutocomplete"
(optionSelected)="selectedPermission($event)"
>
<mat-option
*ngFor="let permission of model.selectablePermissions | async"
[value]="permission"
>
{{ permission }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<values-picker
name="permission"
[control]="model.permissions"
[selection-list]="allPermissions"
></values-picker>
</div>
</div>
@ -86,3 +54,5 @@
</div>
</div>
</form>
<div class="value-picker"></div>

View file

@ -1,3 +1,3 @@
mat-form-field {
values-picker {
width: 100%;
}

View file

@ -1,12 +1,7 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Component, OnInit } from '@angular/core';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute, Router } from '@angular/router';
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
import { HasFailed } from 'picsur-shared/dist/types';
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
import { SnackBarType } from "src/app/models/dto/snack-bar-type.dto";
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UpdateRoleControl } from 'src/app/models/forms/updaterole.control';
import { PermissionService } from 'src/app/services/api/permission.service';
import { RolesService } from 'src/app/services/api/roles.service';
@ -23,11 +18,10 @@ enum EditMode {
styleUrls: ['./settings-roles-edit.component.scss'],
})
export class SettingsRolesEditComponent implements OnInit {
readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
private mode: EditMode = EditMode.edit;
model = new UpdateRoleControl();
allPermissions: string[] = [];
get adding() {
return this.mode === EditMode.add;
@ -49,26 +43,29 @@ export class SettingsRolesEditComponent implements OnInit {
}
private async initRole() {
// Check if adding or editing
const rolename = this.route.snapshot.paramMap.get('role');
if (!rolename) {
this.mode = EditMode.add;
return;
}
// Set data thats already known
this.mode = EditMode.edit;
this.model.putRoleName(rolename);
// Fetch data and populate form
const role = await this.rolesService.getRole(rolename);
if (HasFailed(role)) {
this.utilService.showSnackBar('Failed to get role', SnackBarType.Error);
return;
}
this.model.putRoleName(role.name);
this.model.putPermissions(role.permissions);
}
private async initPermissions() {
// Get a list of all permissions so that we can select them
const allPermissions = await this.permissionsService.fetchAllPermission();
if (HasFailed(allPermissions)) {
this.utilService.showSnackBar(
@ -78,30 +75,13 @@ export class SettingsRolesEditComponent implements OnInit {
return;
}
this.model.putAllPermissions(allPermissions);
}
removePermission(permission: string) {
this.model.removePermission(permission);
}
addPermission(event: MatChipInputEvent) {
const value = (event.value ?? '').trim();
this.model.addPermission(value);
}
selectedPermission(event: MatAutocompleteSelectedEvent): void {
this.model.addPermission(event.option.viewValue);
this.allPermissions = allPermissions;
}
cancel() {
this.router.navigate(['/settings/roles']);
}
uiFriendlyPermission(permission: string) {
return UIFriendlyPermissions[permission as Permission] ?? permission;
}
async updateUser() {
const data = this.model.getData();

View file

@ -1,7 +1,6 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
@ -9,6 +8,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { ValuesPickerModule } from 'src/app/components/values-picker/values-picker.module';
import { SettingsRolesEditComponent } from './settings-roles-edit/settings-roles-edit.component';
import { SettingsRolesComponent } from './settings-roles.component';
import { SettingsRolesRoutingModule } from './settings-roles.routing.module';
@ -27,7 +27,7 @@ import { SettingsRolesRoutingModule } from './settings-roles.routing.module';
MatFormFieldModule,
MatInputModule,
ReactiveFormsModule,
MatAutocompleteModule
ValuesPickerModule
],
})
export class SettingsRolesRouteModule {}

View file

@ -46,45 +46,12 @@
<div class="row" *ngIf="!isLockedPerms()">
<div class="col-lg-6 col-12">
<mat-form-field appearance="outline" color="accent">
<mat-label>Roles</mat-label>
<mat-chip-list #chipList aria-label="Roles Selection">
<mat-chip
*ngFor="let role of model.selectedRoles"
[removable]="model.isRemovable(role)"
[disabled]="!model.isRemovable(role)"
(removed)="removeRole(role)"
>
{{ role }}
<button *ngIf="model.isRemovable(role)" matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
<input
placeholder="Add role..."
#fruitInput
[formControl]="model.rolesControl"
[value]="model.rolesControl.value"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="addRole($event)"
autocorrect="off"
autocapitalize="none"
/>
</mat-chip-list>
<mat-autocomplete
#auto="matAutocomplete"
(optionSelected)="selectedRole($event)"
>
<mat-option
*ngFor="let role of model.selectableRoles | async"
[value]="role"
>
{{ role }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<values-picker
name="role"
[control]="model.roles"
[disabled-list]="soulBoundRoles"
[selection-list]="allRoles"
></values-picker>
</div>
</div>

View file

@ -1,3 +1,3 @@
mat-form-field {
values-picker {
width: 100%;
}

View file

@ -1,12 +1,10 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Component, OnInit } from '@angular/core';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute, Router } from '@angular/router';
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
import { ERole } from 'picsur-shared/dist/entities/role.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
import { SnackBarType } from "src/app/models/dto/snack-bar-type.dto";
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UpdateUserControl } from 'src/app/models/forms/updateuser.control';
import { RolesService } from 'src/app/services/api/roles.service';
import { UserManageService } from 'src/app/services/api/usermanage.service';
@ -24,12 +22,14 @@ enum EditMode {
})
export class SettingsUsersEditComponent implements OnInit {
private ImmutableUsersList: string[] = [];
readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
private mode: EditMode = EditMode.edit;
model = new UpdateUserControl();
allFullRoles: ERole[] = [];
get allRoles(): string[] {
return this.allFullRoles.map((role) => role.name);
}
soulBoundRoles: string[] = [];
get adding() {
return this.mode === EditMode.add;
@ -53,14 +53,21 @@ export class SettingsUsersEditComponent implements OnInit {
private async initUser() {
const username = this.route.snapshot.paramMap.get('username');
const { DefaultRoles, SoulBoundRoles } =
await this.rolesService.getSpecialRolesOptimistic();
this.model.putSoulBoundRoles(SoulBoundRoles);
const SpecialRoles = await this.rolesService.getSpecialRoles();
if (HasFailed(SpecialRoles)) {
this.utilService.showSnackBar(
'Failed to get special roles',
SnackBarType.Error
);
return;
}
this.soulBoundRoles = SpecialRoles.SoulBoundRoles;
if (!username) {
this.mode = EditMode.add;
this.model.putRoles(DefaultRoles);
this.model.putRoles(SpecialRoles.DefaultRoles);
return;
}
@ -88,29 +95,25 @@ export class SettingsUsersEditComponent implements OnInit {
return;
}
this.model.putAllRoles(roles);
this.allFullRoles = roles;
}
getEffectivePermissions() {
return this.model
.getEffectivePermissions()
.map(
(permission) =>
UIFriendlyPermissions[permission as Permission] ?? permission
public getEffectivePermissions(): string[] {
const permissions: string[] = [];
for (const role of this.model.selectedRoles) {
const fullRole = this.allFullRoles.find((r) => r.name === role);
if (!fullRole) {
console.warn(`Role ${role} not found`);
continue;
}
permissions.push(
...fullRole.permissions.filter((p) => !permissions.includes(p))
);
}
}
removeRole(role: string) {
this.model.removeRole(role);
}
addRole(event: MatChipInputEvent) {
const value = (event.value ?? '').trim();
this.model.addRole(value);
}
selectedRole(event: MatAutocompleteSelectedEvent): void {
this.model.addRole(event.option.viewValue);
return permissions.map((p) => UIFriendlyPermissions[p as Permission] ?? p);
}
cancel() {

View file

@ -1,7 +1,6 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
@ -9,15 +8,13 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { ValuesPickerModule } from 'src/app/components/values-picker/values-picker.module';
import { SettingsUsersEditComponent } from './settings-users-edit/settings-users-edit.component';
import { SettingsUsersComponent } from './settings-users.component';
import { SettingsUsersRoutingModule } from './settings-users.routing.module';
@NgModule({
declarations: [
SettingsUsersComponent,
SettingsUsersEditComponent,
],
declarations: [SettingsUsersComponent, SettingsUsersEditComponent],
imports: [
CommonModule,
SettingsUsersRoutingModule,
@ -28,9 +25,9 @@ import { SettingsUsersRoutingModule } from './settings-users.routing.module';
MatFormFieldModule,
MatInputModule,
MatChipsModule,
MatAutocompleteModule,
FormsModule,
ReactiveFormsModule,
ValuesPickerModule,
],
})
export class SettingsUsersRouteModule {}