Add new components for AI and tutorials, update routing, and enhance file upload functionality
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminModuleService } from './admin-module.service';
|
||||
|
||||
describe('AdminModuleService', () => {
|
||||
let service: AdminModuleService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(AdminModuleService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {User} from '../../models/user.model';
|
||||
import {GlobalConstants} from '../../global-constants';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AdminModuleService {
|
||||
baseUrl = GlobalConstants.API_URL;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
|
||||
}
|
||||
|
||||
updateUserRoles(id: any,data: User): Observable<any> {
|
||||
return this.http.put(`${this.baseUrl}/users/${id}`, data);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p><p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p><p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p><p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
<p>admin-welcome.component works!</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminWelcomeComponent } from './admin-welcome.component';
|
||||
|
||||
describe('AdminWelcomeComponent', () => {
|
||||
let component: AdminWelcomeComponent;
|
||||
let fixture: ComponentFixture<AdminWelcomeComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AdminWelcomeComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AdminWelcomeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-welcome.component',
|
||||
imports: [],
|
||||
templateUrl: './admin-welcome.component.html',
|
||||
styleUrl: './admin-welcome.component.scss'
|
||||
})
|
||||
export class AdminWelcomeComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<app-side-bar-admin
|
||||
class="pc-sidebar"
|
||||
>
|
||||
</app-side-bar-admin>
|
||||
<div class="pc-container">
|
||||
<router-outlet />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//---------------
|
||||
.pc-sidebar{
|
||||
top: 65px;
|
||||
overflow-y: auto;
|
||||
background-color: rgba(153, 153, 153, 0.16);
|
||||
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.pc-container{
|
||||
top: 0px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminComponent } from './admin.component';
|
||||
|
||||
describe('AdminComponent', () => {
|
||||
let component: AdminComponent;
|
||||
let fixture: ComponentFixture<AdminComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AdminComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AdminComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
|
||||
import {Router, RouterOutlet} from "@angular/router";
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import { ScrollPanelModule } from 'primeng/scrollpanel';
|
||||
import {MenuItem} from 'primeng/api';
|
||||
import {SideBarAdminComponent} from '../side-bar-admin.component/side-bar-admin.component';
|
||||
@Component({
|
||||
selector: 'app-admin.component',
|
||||
imports: [
|
||||
RouterOutlet,
|
||||
ScrollPanelModule,
|
||||
SideBarAdminComponent
|
||||
],
|
||||
schemas:[CUSTOM_ELEMENTS_SCHEMA],
|
||||
templateUrl: './admin.component.html',
|
||||
styleUrl: './admin.component.scss'
|
||||
})
|
||||
export class AdminComponent implements OnInit {
|
||||
private document = inject<Document>(DOCUMENT);
|
||||
height = 0;
|
||||
|
||||
items: MenuItem[] | undefined;
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.height = 0
|
||||
|
||||
}
|
||||
|
||||
// public props
|
||||
navCollapsed: boolean = false;
|
||||
navCollapsedMob: boolean = false;
|
||||
|
||||
// public method
|
||||
navMobClick() {
|
||||
if (this.navCollapsedMob && !document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
|
||||
this.navCollapsedMob = !this.navCollapsedMob;
|
||||
setTimeout(() => {
|
||||
this.navCollapsedMob = !this.navCollapsedMob;
|
||||
}, 100);
|
||||
} else {
|
||||
this.navCollapsedMob = !this.navCollapsedMob;
|
||||
}
|
||||
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('navbar-collapsed')) {
|
||||
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('navbar-collapsed');
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Escape') {
|
||||
this.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
closeMenu() {
|
||||
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
|
||||
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('mob-open');
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
if(this.document.defaultView !== null || this.document.defaultView !== undefined) {
|
||||
// @ts-ignore
|
||||
this.height = this.document.defaultView.innerHeight;
|
||||
}
|
||||
else
|
||||
this.height = 0;
|
||||
|
||||
this.items = [
|
||||
{
|
||||
label: 'Admin',
|
||||
icon: 'pi pi-palette',
|
||||
command: () => {
|
||||
this.router.navigate(['/main/admin/welcome']);
|
||||
},
|
||||
items: [
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: 'pi pi-eraser',
|
||||
// route: '/main/admin/settings'
|
||||
command: () => {
|
||||
this.router.navigate(['/main/admin/settings']);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'System',
|
||||
icon: 'pi pi-heart',
|
||||
route: '/main/admin/system'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
|
||||
import {mainRouting} from '../main-module/main.routing';
|
||||
import {MainComponent} from '../main-module/main.component/main.component';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {AdminComponent} from './admin.component/admin.component';
|
||||
import {adminRouting} from './admin.routing';
|
||||
import { ScrollPanelModule } from 'primeng/scrollpanel';
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
||||
],
|
||||
imports: [adminRouting, AdminComponent,ScrollPanelModule],
|
||||
exports: [RouterModule],
|
||||
schemas:[CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class AdminModule {}
|
||||
@@ -0,0 +1,30 @@
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {AdminComponent} from './admin.component/admin.component';
|
||||
|
||||
const ADMIN_ROUTES: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AdminComponent,
|
||||
|
||||
children: [
|
||||
{
|
||||
path: 'admin-welcome',
|
||||
loadComponent: () => import('../admin-module/admin-welcome.component/admin-welcome.component').then((c) => c.AdminWelcomeComponent),
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
loadComponent: () => import('../admin-module/settings.component/settings.component').then((c) => c.SettingsComponent)
|
||||
},
|
||||
{
|
||||
path: 'system',
|
||||
loadComponent: () => import('../admin-module/system.component/system.component').then((c) => c.SystemComponent)
|
||||
},
|
||||
{
|
||||
path: 'users',
|
||||
loadComponent: () => import('../admin-module/users.component/users.component').then((c) => c.UsersComponent)
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const adminRouting = RouterModule.forChild(ADMIN_ROUTES);
|
||||
@@ -0,0 +1,27 @@
|
||||
<p>settings.component works!</p>
|
||||
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Data Source properties">
|
||||
|
||||
<table mat-table
|
||||
[dataSource]="properties" multiTemplateDataRows="true" class="mat-elevation-z8">
|
||||
@for (column of displayedColumns; track column) {
|
||||
<ng-container matColumnDef="{{column}}">
|
||||
<th mat-header-cell *matHeaderCellDef>{{column}}</th>
|
||||
<td mat-cell *matCellDef="let element ; let k = dataIndex;">{{element[column]}}</td>
|
||||
</ng-container>
|
||||
}
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let element; columns: displayedColumns;"
|
||||
class="example-element-row"
|
||||
>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SettingsComponent } from './settings.component';
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
let component: SettingsComponent;
|
||||
let fixture: ComponentFixture<SettingsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SettingsComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {MatCard, MatCardContent} from '@angular/material/card';
|
||||
import {MatDivider} from '@angular/material/divider';
|
||||
import {MatTab, MatTabGroup} from '@angular/material/tabs';
|
||||
import {
|
||||
MatCell,
|
||||
MatCellDef,
|
||||
MatColumnDef,
|
||||
MatHeaderCell, MatHeaderCellDef,
|
||||
MatHeaderRow,
|
||||
MatHeaderRowDef,
|
||||
MatRow, MatRowDef, MatTable
|
||||
} from '@angular/material/table';
|
||||
import {SystemService} from '../../../services/system.service';
|
||||
import {NameValueItem} from '../../../models/name-value-item';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings.component',
|
||||
imports: [
|
||||
MatCard,
|
||||
MatCardContent,
|
||||
MatDivider,
|
||||
MatTab,
|
||||
MatTabGroup,
|
||||
MatCell,
|
||||
MatCellDef,
|
||||
MatColumnDef,
|
||||
MatHeaderCell,
|
||||
|
||||
MatTable,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderRow,
|
||||
MatHeaderRowDef,
|
||||
MatRow,
|
||||
MatRowDef
|
||||
],
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrl: './settings.component.scss'
|
||||
})
|
||||
export class SettingsComponent {
|
||||
properties: NameValueItem[] = [] ;
|
||||
|
||||
displayedColumns: string[] = ['name', 'value'];
|
||||
|
||||
|
||||
constructor(private systemService: SystemService) {
|
||||
this.retrieveProperties();
|
||||
}
|
||||
|
||||
|
||||
retrieveProperties(): void {
|
||||
this.systemService.getDataSourceProperties()
|
||||
.subscribe(
|
||||
(data: NameValueItem[]) => {
|
||||
this.properties = data;
|
||||
console.log(data);
|
||||
},
|
||||
(error: any) => {
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<mat-nav-list>
|
||||
<a mat-list-item routerLink="admin-welcome">
|
||||
<span class="entry">
|
||||
<mat-icon>house</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Dashboard</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
<a mat-list-item routerLink="users">
|
||||
<span class="entry">
|
||||
<mat-icon>newspaper</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Users</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
<a mat-list-item routerLink="settings">
|
||||
<span class="entry">
|
||||
<mat-icon>newspaper</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Settings</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
<a mat-list-item routerLink="system">
|
||||
<span class="entry">
|
||||
<mat-icon>newspaper</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >System</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
</mat-nav-list>
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
.entry{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding:0.75rem;
|
||||
color: rgba(24, 255, 255, 0.96);
|
||||
|
||||
}
|
||||
|
||||
a.mdc-list-item
|
||||
{
|
||||
|
||||
background-color: rgba(24,255,255,0.04);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SideBarAdminComponent } from './side-bar-admin.component';
|
||||
|
||||
describe('SideBarAdminComponent', () => {
|
||||
let component: SideBarAdminComponent;
|
||||
let fixture: ComponentFixture<SideBarAdminComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SideBarAdminComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SideBarAdminComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {MatListItem, MatNavList} from '@angular/material/list';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'app-side-bar-admin',
|
||||
imports: [
|
||||
MatIcon,
|
||||
MatListItem,
|
||||
MatNavList,
|
||||
RouterLink
|
||||
],
|
||||
templateUrl: './side-bar-admin.component.html',
|
||||
styleUrl: './side-bar-admin.component.scss',
|
||||
schemas:[CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class SideBarAdminComponent {
|
||||
isCollapsed = false;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Custom beans">
|
||||
Content 1
|
||||
<table mat-table
|
||||
[dataSource]="customBeans" multiTemplateDataRows="true" class="mat-elevation-z8">
|
||||
@for (column of displayedColumns; track column) {
|
||||
<ng-container matColumnDef="{{column}}">
|
||||
<th mat-header-cell *matHeaderCellDef>{{column}}</th>
|
||||
<td mat-cell *matCellDef="let element ; let k = dataIndex;">{{element[column]}}</td>
|
||||
</ng-container>
|
||||
}
|
||||
<ng-container matColumnDef="expand">
|
||||
<th mat-header-cell *matHeaderCellDef aria-label="row actions"> </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button
|
||||
matIconButton
|
||||
aria-label="expand row"
|
||||
(click)="toggle(element); $event.stopPropagation()"
|
||||
class="example-toggle-button"
|
||||
[class.example-toggle-button-expanded]="isExpanded(element)">
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
|
||||
<ng-container matColumnDef="expandedDetail">
|
||||
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
|
||||
<div class="example-element-detail-wrapper"
|
||||
[class.example-element-detail-wrapper-expanded]="isExpanded(element)">
|
||||
<div class="example-element-detail">
|
||||
|
||||
<mat-list role="list">
|
||||
<mat-list-item role="listitem">Name: {{element.name}}</mat-list-item>
|
||||
<mat-list-item role="listitem">Type: {{element.type}}</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="columnsToDisplayWithExpand"></tr>
|
||||
<tr mat-row *matRowDef="let element; columns: columnsToDisplayWithExpand;"
|
||||
class="example-element-row"
|
||||
[class.example-expanded-row]="isExpanded(element)"
|
||||
(click)="toggle(element)">
|
||||
</tr>
|
||||
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
|
||||
</table>
|
||||
|
||||
</mat-tab>
|
||||
<mat-tab label="All beans">
|
||||
Content 2
|
||||
<table mat-table
|
||||
[dataSource]="allBeans" multiTemplateDataRows="true" class="mat-elevation-z8">
|
||||
@for (column of displayedColumns; track column) {
|
||||
<ng-container matColumnDef="{{column}}">
|
||||
<th mat-header-cell *matHeaderCellDef>{{column}}</th>
|
||||
<td mat-cell *matCellDef="let element ; let k = dataIndex;">{{element[column]}}</td>
|
||||
</ng-container>
|
||||
}
|
||||
<ng-container matColumnDef="expand">
|
||||
<th mat-header-cell *matHeaderCellDef aria-label="row actions"> </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button
|
||||
matIconButton
|
||||
aria-label="expand row"
|
||||
(click)="toggle(element); $event.stopPropagation()"
|
||||
class="example-toggle-button"
|
||||
[class.example-toggle-button-expanded]="isExpanded(element)">
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
|
||||
<ng-container matColumnDef="expandedDetail">
|
||||
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
|
||||
<div class="example-element-detail-wrapper"
|
||||
[class.example-element-detail-wrapper-expanded]="isExpanded(element)">
|
||||
<div class="example-element-detail">
|
||||
|
||||
<mat-list role="list">
|
||||
<mat-list-item role="listitem">Name: {{element.name}}</mat-list-item>
|
||||
<mat-list-item role="listitem">Type: {{element.type}}</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="columnsToDisplayWithExpand"></tr>
|
||||
<tr mat-row *matRowDef="let element; columns: columnsToDisplayWithExpand;"
|
||||
class="example-element-row"
|
||||
[class.example-expanded-row]="isExpanded(element)"
|
||||
(click)="toggle(element)">
|
||||
</tr>
|
||||
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
|
||||
</table>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@@ -0,0 +1,113 @@
|
||||
mat-card{
|
||||
//margin: 20px;
|
||||
background-color: rgba(240, 248, 255, 0.7);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
mat-card-title{
|
||||
color: cyan;
|
||||
}
|
||||
|
||||
mat-card-subtitle{
|
||||
color: #009dff;
|
||||
}
|
||||
|
||||
mat-tab{
|
||||
color:black;
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
color:lightgrey;
|
||||
}
|
||||
|
||||
|
||||
mat-list{
|
||||
//background-color: #1389d3;
|
||||
color:black;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
mat-list-item{
|
||||
margin: 10px 10px 10px 10px;
|
||||
background-color: #447694;
|
||||
|
||||
color: #009dff;
|
||||
}
|
||||
|
||||
tr.example-detail-row {
|
||||
height: 0;
|
||||
background: #fffdfd;
|
||||
}
|
||||
|
||||
tr.example-element-row {
|
||||
cursor: pointer;
|
||||
color: #1389d3;
|
||||
background: #fffdfd;
|
||||
}
|
||||
|
||||
tr.example-element-row:not(.example-expanded-row):hover {
|
||||
background: whitesmoke;
|
||||
}
|
||||
|
||||
tr.example-element-row:not(.example-expanded-row):active {
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.example-element-row td {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.example-element-detail-wrapper {
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: 0fr;
|
||||
grid-template-columns: 100%;
|
||||
transition: grid-template-rows 225ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.example-element-detail-wrapper-expanded {
|
||||
grid-template-rows: 1fr;
|
||||
|
||||
}
|
||||
|
||||
.example-element-detail {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.example-element-diagram {
|
||||
min-width: 80px;
|
||||
border: 2px solid black;
|
||||
padding: 8px;
|
||||
font-weight: lighter;
|
||||
margin: 8px 0;
|
||||
height: 104px;
|
||||
}
|
||||
|
||||
.example-element-symbol {
|
||||
font-weight: bold;
|
||||
font-size: 40px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.example-element-description {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.example-element-description-attribution {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.example-toggle-button {
|
||||
transition: transform 225ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.example-toggle-button-expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SystemComponent } from './system.component';
|
||||
|
||||
describe('SystemComponent', () => {
|
||||
let component: SystemComponent;
|
||||
let fixture: ComponentFixture<SystemComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SystemComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SystemComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,137 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {MatCard, MatCardContent} from '@angular/material/card';
|
||||
import {MatDivider} from '@angular/material/divider';
|
||||
import {MatTab, MatTabGroup} from '@angular/material/tabs';
|
||||
import {
|
||||
MatCell,
|
||||
MatCellDef,
|
||||
MatColumnDef,
|
||||
MatHeaderCell, MatHeaderCellDef,
|
||||
MatHeaderRow, MatHeaderRowDef,
|
||||
MatRow,
|
||||
MatRowDef,
|
||||
MatTable
|
||||
} from '@angular/material/table';
|
||||
import {MatList, MatListItem} from '@angular/material/list';
|
||||
import {MatIconButton} from '@angular/material/button';
|
||||
import {SystemService} from '../../../services/system.service';
|
||||
import {Bean} from '../../../models/Bean';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'app-system.component',
|
||||
imports: [
|
||||
MatCard,
|
||||
MatCardContent,
|
||||
MatDivider,
|
||||
MatTabGroup,
|
||||
MatTab,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatIcon,
|
||||
MatCell,
|
||||
MatCellDef,
|
||||
MatList,
|
||||
MatListItem,
|
||||
MatRow,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatHeaderCell,
|
||||
MatHeaderCellDef,
|
||||
MatIconButton,
|
||||
MatIcon,
|
||||
MatHeaderRowDef
|
||||
],
|
||||
templateUrl: './system.component.html',
|
||||
styleUrl: './system.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class SystemComponent {
|
||||
customBeans:Bean[] = [] ;
|
||||
allBeans:Bean[] = [] ;
|
||||
displayedColumns: string[] = ['shortName', 'typeShortName', 'scope'];
|
||||
|
||||
columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
|
||||
|
||||
expandedElement: Bean | null = null;
|
||||
|
||||
resultsLength = 0;
|
||||
isLoadingResults = true;
|
||||
isRateLimitReached = false;
|
||||
|
||||
constructor(private systemService: SystemService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.retrieveBeans();
|
||||
}
|
||||
|
||||
retrieveBeans(): void {
|
||||
this.systemService.getCustomBeans()
|
||||
.subscribe(
|
||||
(data: Bean[]) => {
|
||||
this.customBeans = data;
|
||||
console.log(data);
|
||||
},
|
||||
(error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
this.systemService.getAllBeans()
|
||||
.subscribe(
|
||||
(data: Bean[]) => {
|
||||
this.allBeans = data;
|
||||
console.log(data);
|
||||
},
|
||||
(error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
/** Checks whether an element is expanded. */
|
||||
isExpanded(element: Bean) {
|
||||
return this.expandedElement === element;
|
||||
}
|
||||
|
||||
/** Toggles the expanded state of an element. */
|
||||
toggle(element: Bean) {
|
||||
this.expandedElement = this.isExpanded(element) ? null : element;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//ngAfterViewInit() {
|
||||
//this.exampleDatabase = new ExampleHttpDatabase(this._httpClient);
|
||||
|
||||
// If the user changes the sort order, reset back to the first page.
|
||||
/* this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0));
|
||||
|
||||
merge(this.sort.sortChange, this.paginator.page)
|
||||
.pipe(
|
||||
startWith({}),
|
||||
switchMap(() => {
|
||||
this.isLoadingResults = true;
|
||||
return this.exampleDatabase!.getRepoIssues(
|
||||
this.sort.active,
|
||||
this.sort.direction,
|
||||
this.paginator.pageIndex,
|
||||
).pipe(catchError(() => observableOf(null)));
|
||||
}),
|
||||
map(data => {
|
||||
// Flip flag to show that loading has finished.
|
||||
this.isLoadingResults = false;
|
||||
this.isRateLimitReached = data === null;
|
||||
|
||||
if (data === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Only refresh the result length if there is new data. In case of rate
|
||||
// limit errors, we do not want to reset the paginator to zero, as that
|
||||
// would prevent users from re-triggering requests.
|
||||
this.resultsLength = data.total_count;
|
||||
return data.items;
|
||||
}),
|
||||
)
|
||||
.subscribe(data => (this.data = data));*/
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<table mat-table [dataSource]="users">
|
||||
@for (column of columnsSchema; track column){
|
||||
<ng-container [matColumnDef]="column.key">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{column.label}}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@switch (column.type) {
|
||||
@case ('list') {
|
||||
<mat-form-field class="example-chip-list">
|
||||
<mat-chip-grid #chipGrid aria-label="Role selection">
|
||||
@for (role of element.roles; track $index) {
|
||||
<mat-chip-row (removed)="remove(role,element)">
|
||||
{{role.name}}
|
||||
|
||||
<mat-icon
|
||||
matChipRemove
|
||||
[attr.aria-label]="'remove ' + role.name"
|
||||
>
|
||||
cancel
|
||||
</mat-icon>
|
||||
|
||||
</mat-chip-row>
|
||||
}
|
||||
</mat-chip-grid>
|
||||
<input
|
||||
name="currentRole"
|
||||
placeholder="Add role..."
|
||||
[matChipInputFor]="chipGrid"
|
||||
[matAutocomplete]="auto"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[formControl]="rolesControl"
|
||||
[matChipInputAddOnBlur]="true"
|
||||
(matChipInputTokenEnd)="add($event,element)"
|
||||
(input)="change($event,filteredRoles(element.roles))"
|
||||
/>
|
||||
<mat-autocomplete [formControl]="ac"
|
||||
autoActiveFirstOption
|
||||
#auto
|
||||
(optionSelected)="selected(element,$event); ">
|
||||
@for (role of filteredRoles(element.roles); track role) {
|
||||
<mat-option [value]="role">{{role.name}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
}
|
||||
@default {
|
||||
{{ element[column.key] }}
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
}
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
@@ -0,0 +1,3 @@
|
||||
.example-chip-list {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UsersComponent } from './users.component';
|
||||
|
||||
describe('UsersComponent', () => {
|
||||
let component: UsersComponent;
|
||||
let fixture: ComponentFixture<UsersComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UsersComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UsersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,182 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, model} from '@angular/core';
|
||||
import {UserService} from '../../../services/user.service';
|
||||
import {RolesService} from '../../../services/roles.service';
|
||||
import {Role} from '../../../models/role';
|
||||
import {User} from '../../../models/user.model';
|
||||
import {
|
||||
MatCell,
|
||||
MatCellDef, MatColumnDef,
|
||||
MatHeaderCell, MatHeaderCellDef,
|
||||
MatHeaderRow,
|
||||
MatHeaderRowDef,
|
||||
MatRow,
|
||||
MatRowDef, MatTable
|
||||
} from '@angular/material/table';
|
||||
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {
|
||||
MatAutocomplete,
|
||||
MatAutocompleteSelectedEvent,
|
||||
MatAutocompleteTrigger,
|
||||
MatOption
|
||||
} from '@angular/material/autocomplete';
|
||||
import {
|
||||
MatChip,
|
||||
MatChipGrid,
|
||||
MatChipInput,
|
||||
MatChipInputEvent,
|
||||
MatChipRow,
|
||||
MatChipsModule
|
||||
} from '@angular/material/chips';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {COMMA, ENTER} from '@angular/cdk/keycodes';
|
||||
import {MatFormField} from '@angular/material/input';
|
||||
import {MatSelect, MatSelectTrigger} from '@angular/material/select';
|
||||
import {AdminModuleService} from '../admin-module.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-users.component',
|
||||
imports: [
|
||||
MatCell,
|
||||
MatCellDef,
|
||||
MatHeaderCell,
|
||||
MatHeaderRow,
|
||||
MatHeaderRowDef,
|
||||
MatRow,
|
||||
MatRowDef,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatChipGrid,
|
||||
MatChipRow,
|
||||
MatIcon,
|
||||
MatAutocompleteTrigger,
|
||||
MatChipInput,
|
||||
MatAutocomplete,
|
||||
MatOption,
|
||||
MatFormField,
|
||||
MatChipsModule
|
||||
],
|
||||
templateUrl: './users.component.html',
|
||||
styleUrl: './users.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class UsersComponent {
|
||||
|
||||
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
allRoles:Role[] = [];
|
||||
users: User[] = [];
|
||||
|
||||
displayedColumns: string[] = UserColumns.map((col) => col.key)
|
||||
columnsSchema: any = UserColumns
|
||||
|
||||
readonly currentRole = model('');
|
||||
|
||||
rolesControl = new FormControl([]);
|
||||
protected ac = new FormControl('');
|
||||
|
||||
constructor(private userService: UserService,
|
||||
private roleService: RolesService,
|
||||
private adminService:AdminModuleService) {
|
||||
this.getAllRoles();
|
||||
this.getUsers();
|
||||
}
|
||||
|
||||
getAllRoles():any{
|
||||
this.roleService.getAllRoles().subscribe(
|
||||
(data : any) => {
|
||||
this.allRoles = data;
|
||||
console.log(data);
|
||||
},
|
||||
(err : any)=> {
|
||||
this.allRoles = JSON.parse(err.error).message;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getUsers(){
|
||||
this.userService.getAdminBoard().subscribe(
|
||||
(data : any) => {
|
||||
this.users = data;
|
||||
console.log(data);
|
||||
},
|
||||
(err : any)=> {
|
||||
console.log(JSON.parse(err.error).message);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
filteredRoles(roles : Role[]):any {
|
||||
return this.allRoles.filter(
|
||||
(r:Role) => !roles.some((item) => item.id === r.id),
|
||||
);
|
||||
}
|
||||
|
||||
change($event: Event, roles: Role[]) {
|
||||
roles.filter(
|
||||
(r:Role) => !roles.some((item) => item.name?.toLowerCase() === r.name?.toLowerCase()),
|
||||
)
|
||||
}
|
||||
|
||||
selected(user: User, $event: MatAutocompleteSelectedEvent) {
|
||||
user.roles.push($event.option.value);
|
||||
this.saveUserRoles(user);
|
||||
this.currentRole.set('');
|
||||
$event.option.deselect();
|
||||
}
|
||||
|
||||
add($event: MatChipInputEvent, user:User) {
|
||||
/*const value = ($event.value || '').trim();
|
||||
|
||||
const role = this.allRoles.find(value1 => value1.name === value);
|
||||
if(role){
|
||||
user.roles.push(value);
|
||||
this.saveUserRoles(user);
|
||||
}*/
|
||||
// Clear the input value
|
||||
this.currentRole.set('');
|
||||
}
|
||||
|
||||
remove(role: any, user: User) {
|
||||
const updatedRoles: Role[] = user.roles.filter(
|
||||
(r: Role) => r.id !== role.id // Simple comparison with the role to remove
|
||||
);
|
||||
|
||||
if (updatedRoles && updatedRoles.length >= 1) {
|
||||
user.roles = updatedRoles;
|
||||
this.saveUserRoles(user);
|
||||
}
|
||||
}
|
||||
|
||||
saveUserRoles(user:User){
|
||||
this.adminService.updateUserRoles(user.id, user).subscribe(
|
||||
(data:any) => {
|
||||
console.log(data);
|
||||
},
|
||||
(err:any) => {
|
||||
console.log(err);
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const UserColumns = [
|
||||
{
|
||||
key: 'username',
|
||||
type: 'text',
|
||||
label: 'Name',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
type: 'text',
|
||||
label: 'Email',
|
||||
},
|
||||
{
|
||||
key: 'roles',
|
||||
type: 'list',
|
||||
label: 'Roles',
|
||||
required: true,
|
||||
}
|
||||
];
|
||||
Reference in New Issue
Block a user