Merge pull request '[ogGui_519] Mapa de aulas' (#5) from oggui/groups into main

Reviewed-on: #5
pull/6/head
Alvaro Puente Mella 2024-07-22 16:24:36 +02:00
commit e90316161c
8 changed files with 207 additions and 75 deletions

View File

@ -13,8 +13,8 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { CustomInterceptor } from './services/custom.interceptor'; import { CustomInterceptor } from './services/custom.interceptor';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import {MatToolbarModule} from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import {MatIconModule} from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatSidenavModule } from '@angular/material/sidenav'; import { MatSidenavModule } from '@angular/material/sidenav';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -26,37 +26,37 @@ import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list';
import { UsersComponent } from './components/pages/admin/users/users/users.component'; import { UsersComponent } from './components/pages/admin/users/users/users.component';
import { RolesComponent } from './components/pages/admin/roles/roles/roles.component'; import { RolesComponent } from './components/pages/admin/roles/roles/roles.component';
import {MatTableModule} from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import {MatDialogModule} from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { DeleteUserModalComponent } from './components/pages/admin/users/users/delete-user-modal/delete-user-modal.component'; import { DeleteUserModalComponent } from './components/pages/admin/users/users/delete-user-modal/delete-user-modal.component';
import { AddUserModalComponent } from './components/pages/admin/users/users/add-user-modal/add-user-modal.component'; import { AddUserModalComponent } from './components/pages/admin/users/users/add-user-modal/add-user-modal.component';
import {MatSelectModule} from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { EditUserModalComponent } from './components/pages/admin/users/users/edit-user-modal/edit-user-modal.component'; import { EditUserModalComponent } from './components/pages/admin/users/users/edit-user-modal/edit-user-modal.component';
import { AddRoleModalComponent } from './components/pages/admin/roles/roles/add-role-modal/add-role-modal.component'; import { AddRoleModalComponent } from './components/pages/admin/roles/roles/add-role-modal/add-role-modal.component';
import { DeleteRoleModalComponent } from './components/pages/admin/roles/roles/delete-role-modal/delete-role-modal.component'; import { DeleteRoleModalComponent } from './components/pages/admin/roles/roles/delete-role-modal/delete-role-modal.component';
import { ChangePasswordModalComponent } from './components/pages/admin/users/users/change-password-modal/change-password-modal.component'; import { ChangePasswordModalComponent } from './components/pages/admin/users/users/change-password-modal/change-password-modal.component';
import { GroupsComponent } from './components/groups/groups.component'; import { GroupsComponent } from './components/groups/groups.component';
import {MatDividerModule} from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { CreateOrganizationalUnitComponent } from './components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component'; import { CreateOrganizationalUnitComponent } from './components/groups/organizational-units/create-organizational-unit/create-organizational-unit.component';
import {MatStepperModule} from '@angular/material/stepper'; import { MatStepperModule } from '@angular/material/stepper';
import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { CreateClientComponent } from './components/groups/clients/create-client/create-client.component'; import { CreateClientComponent } from './components/groups/clients/create-client/create-client.component';
import { DeleteModalComponent } from './components/groups/delete-modal/delete-modal.component'; import { DeleteModalComponent } from './components/groups/delete-modal/delete-modal.component';
import { EditOrganizationalUnitComponent } from './components/groups/organizational-units/edit-organizational-unit/edit-organizational-unit.component'; import { EditOrganizationalUnitComponent } from './components/groups/organizational-units/edit-organizational-unit/edit-organizational-unit.component';
import { EditClientComponent } from './components/groups/clients/edit-client/edit-client.component'; import { EditClientComponent } from './components/groups/clients/edit-client/edit-client.component';
import { ClassroomViewComponent } from './components/groups/classroom-view/classroom-view.component'; import { ClassroomViewComponent } from './components/groups/classroom-view/classroom-view.component';
import {MatProgressSpinner} from "@angular/material/progress-spinner"; import { MatProgressSpinner } from "@angular/material/progress-spinner";
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu"; import { MatMenu, MatMenuItem, MatMenuTrigger } from "@angular/material/menu";
import {MatAutocomplete} from "@angular/material/autocomplete"; import { MatAutocomplete } from "@angular/material/autocomplete";
import {MatChip, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule} from "@angular/material/chips"; import { MatChip, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule } from "@angular/material/chips";
import { ClientViewComponent } from './components/groups/client-view/client-view.component'; import { ClientViewComponent } from './components/groups/client-view/client-view.component';
import {MatTab, MatTabGroup} from "@angular/material/tabs"; import { MatTab, MatTabGroup } from "@angular/material/tabs";
import {MatTooltip} from "@angular/material/tooltip"; import { MatTooltip } from "@angular/material/tooltip";
import { DeleteGroupsModalComponent } from './components/groups/delete-groups-modal/delete-groups-modal.component'; import { DeleteGroupsModalComponent } from './components/groups/delete-groups-modal/delete-groups-modal.component';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { ToastrModule } from 'ngx-toastr'; import { ToastrModule } from 'ngx-toastr';
import { ShowOrganizationalUnitComponent } from './components/groups/organizational-units/show-organizational-unit/show-organizational-unit.component'; import { ShowOrganizationalUnitComponent } from './components/groups/organizational-units/show-organizational-unit/show-organizational-unit.component';
import {MatGridList} from "@angular/material/grid-list"; import { MatGridList } from "@angular/material/grid-list";
import { TreeViewComponent } from './components/groups/tree-view/tree-view.component'; import { TreeViewComponent } from './components/groups/tree-view/tree-view.component';
import { import {
MatNestedTreeNode, MatNestedTreeNode,
@ -67,38 +67,41 @@ import {
MatTreeNodeToggle MatTreeNodeToggle
} from "@angular/material/tree"; } from "@angular/material/tree";
import { LegendComponent } from './components/groups/legend/legend.component'; import { LegendComponent } from './components/groups/legend/legend.component';
import { ClassroomViewDialogComponent } from './components/groups/classroom-view/classroom-view-modal';
@NgModule({ declarations: [ @NgModule({
AppComponent, declarations: [
AuthLayoutComponent, AppComponent,
MainLayoutComponent, AuthLayoutComponent,
HeaderComponent, MainLayoutComponent,
SidebarComponent, HeaderComponent,
LoginComponent, SidebarComponent,
AdminComponent, LoginComponent,
MainLayoutComponent, AdminComponent,
UsersComponent, MainLayoutComponent,
RolesComponent, UsersComponent,
DeleteUserModalComponent, RolesComponent,
AddUserModalComponent, DeleteUserModalComponent,
EditUserModalComponent, AddUserModalComponent,
AddRoleModalComponent, EditUserModalComponent,
DeleteRoleModalComponent, AddRoleModalComponent,
ChangePasswordModalComponent, DeleteRoleModalComponent,
GroupsComponent, ChangePasswordModalComponent,
CreateOrganizationalUnitComponent, GroupsComponent,
CreateClientComponent, CreateOrganizationalUnitComponent,
DeleteModalComponent, CreateClientComponent,
EditOrganizationalUnitComponent, DeleteModalComponent,
EditClientComponent, EditOrganizationalUnitComponent,
ClassroomViewComponent, EditClientComponent,
ClientViewComponent, ClassroomViewComponent,
DeleteGroupsModalComponent, ClientViewComponent,
ShowOrganizationalUnitComponent, DeleteGroupsModalComponent,
TreeViewComponent, ShowOrganizationalUnitComponent,
LegendComponent TreeViewComponent,
], LegendComponent,
bootstrap: [AppComponent], ClassroomViewDialogComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule, imports: [BrowserModule,
AppRoutingModule, AppRoutingModule,
FormsModule, FormsModule,
@ -118,6 +121,7 @@ import { LegendComponent } from './components/groups/legend/legend.component';
MatSelectModule, MatSelectModule,
MatDividerModule, MatDividerModule,
MatStepperModule, MatStepperModule,
DragDropModule,
MatSlideToggleModule, MatMenu, MatMenuTrigger, MatMenuItem, MatAutocomplete, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule, MatChip, MatProgressSpinner, MatTabGroup, MatTab, MatTooltip, MatSlideToggleModule, MatMenu, MatMenuTrigger, MatMenuItem, MatAutocomplete, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule, MatChip, MatProgressSpinner, MatTabGroup, MatTab, MatTooltip,
ToastrModule.forRoot( ToastrModule.forRoot(
{ {
@ -130,16 +134,17 @@ import { LegendComponent } from './components/groups/legend/legend.component';
} }
), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet ), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet
], ],
schemas: [ schemas: [
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
], ],
providers: [ providers: [
{ {
provide: HTTP_INTERCEPTORS, provide: HTTP_INTERCEPTORS,
useClass: CustomInterceptor, useClass: CustomInterceptor,
multi: true multi: true
}, },
provideAnimationsAsync(), provideAnimationsAsync(),
provideHttpClient(withInterceptorsFromDi()) provideHttpClient(withInterceptorsFromDi())
], }) ],
})
export class AppModule { } export class AppModule { }

View File

@ -0,0 +1,35 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-classroom-view-dialog',
template: `
<h2 mat-dialog-title>Plano de {{ classroomName }}</h2>
<mat-dialog-content>
<app-classroom-view [clients]="data.clients"></app-classroom-view>
</mat-dialog-content>
`,
styles: [`
mat-dialog-content {
overflow: hidden;
}
`]
})
export class ClassroomViewDialogComponent {
classroomName: string | undefined;
constructor(@Inject(MAT_DIALOG_DATA) public data: any) {
console.log('ClassroomViewDialogComponent');
console.log(data);
}
ngOnInit() {
if (this.data.clients && this.data.clients.length > 0) {
this.classroomName = this.data.clients[0].organizationalUnit.name;
} else {
this.classroomName = 'N/A';
}
}
}

View File

@ -2,7 +2,8 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 10px; gap: 10px;
min-height: 43vh; height: 90%;
border: 3px solid black;
} }
.classroom-group { .classroom-group {
@ -26,7 +27,7 @@ mat-card {
.proyector-image { .proyector-image {
width: auto; width: auto;
height: 100px; /* Ajusta la altura según sea necesario */ height: 100px;
} }
.client-info { .client-info {
@ -37,8 +38,8 @@ mat-card {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: rgba(0, 0, 0, 0); /* Fondo semitransparente para el texto */ background-color: rgba(0, 0, 0, 0);
color: black; /* Color del texto */ color: black;
text-align: center; text-align: center;
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
@ -121,7 +122,7 @@ mat-chip {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
margin-right: 20px; /* Espacio entre la pizarra y el proyector */ margin-right: 20px;
} }
.misc-clients { .misc-clients {
@ -130,3 +131,7 @@ mat-chip {
justify-content: center; justify-content: center;
margin-bottom: 25px; margin-bottom: 25px;
} }
.saveDisposition-btn{
margin-top: 10px;
}

View File

@ -1,13 +1,14 @@
<div class="classroom"> <div class="classroom">
<mat-card *ngFor="let group of groupedClients" class="classroom-group"> <mat-card *ngFor="let group of groupedClients" class="classroom-group">
<mat-card-title>Clientes dentro de: {{ group.organizationalUnitName }}</mat-card-title>
<div class="misc-clients"> <div class="misc-clients">
<div class="classroom-board">Pizarra digital</div> <div class="classroom-board" cdkDrag cdkDragBoundary=".classroom">Pizarra digital</div>
<img mat-card-image src="assets/images/proyector.png" alt="Proyector" class="proyector-image"/> <img mat-card-image src="assets/images/proyector.png" alt="Proyector" class="proyector-image" cdkDrag cdkDragBoundary=".classroom"/>
</div> </div>
<div *ngFor="let row of group.clientRows" class="client-row"> <div *ngFor="let row of group.clientRows" class="client-row">
<div class="client-container" *ngFor="let client of row"> <div class="client-container" *ngFor="let client of row"
<div class="client-box" (click)="handleClientClick(client)"> cdkDrag [cdkDragFreeDragPosition]="client.dragPosition" (cdkDragMoved)="onDragMoved($event, client)" cdkDragBoundary=".classroom">
<div class="client-box" (dblclick)="handleClientClick(client)">
<mat-card appearance="outlined"> <mat-card appearance="outlined">
<div class="client-image-container"> <div class="client-image-container">
<img mat-card-image src="assets/images/client.png" alt="Client" class="client-image"/> <img mat-card-image src="assets/images/client.png" alt="Client" class="client-image"/>
@ -24,3 +25,4 @@
</div> </div>
</mat-card> </mat-card>
</div> </div>
<button mat-flat-button class="saveDisposition-btn" color="primary" (click)="saveDisposition()">Guardar disposición</button>

View File

@ -1,6 +1,9 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit, OnChanges } from '@angular/core';
import {MatDialog} from "@angular/material/dialog"; import { MatDialog } from '@angular/material/dialog';
import {ClientViewComponent} from "../client-view/client-view.component"; import { ClientViewComponent } from "../client-view/client-view.component";
import { CdkDragMove } from '@angular/cdk/drag-drop';
import { HttpClient } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';
interface GroupedClients { interface GroupedClients {
organizationalUnitName: string; organizationalUnitName: string;
@ -12,19 +15,23 @@ interface GroupedClients {
templateUrl: './classroom-view.component.html', templateUrl: './classroom-view.component.html',
styleUrls: ['./classroom-view.component.css'] styleUrls: ['./classroom-view.component.css']
}) })
export class ClassroomViewComponent implements OnInit { export class ClassroomViewComponent implements OnInit, OnChanges {
@Input() clients: any[] = []; @Input() clients: any[] = [];
@Input() pcInTable: number = 5; @Input() pcInTable: number = 5;
groupedClients: GroupedClients[] = []; groupedClients: GroupedClients[] = [];
constructor(public dialog: MatDialog) {} constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService) {}
ngOnInit(): void { ngOnInit(): void {
this.groupClientsByOrganizationalUnit(); this.groupClientsByOrganizationalUnit();
this.initializeClientPositions();
} }
ngOnChanges(): void { ngOnChanges(): void {
this.groupClientsByOrganizationalUnit(); this.groupClientsByOrganizationalUnit();
this.initializeClientPositions();
} }
groupClientsByOrganizationalUnit(): void { groupClientsByOrganizationalUnit(): void {
@ -41,8 +48,21 @@ export class ClassroomViewComponent implements OnInit {
organizationalUnitName: ouName, organizationalUnitName: ouName,
clientRows: this.chunkArray(grouped[ouName], this.pcInTable) clientRows: this.chunkArray(grouped[ouName], this.pcInTable)
})); }));
console.log(this.groupedClients);
} }
initializeClientPositions(): void {
this.groupedClients.forEach(group => {
group.clientRows.forEach(row => {
row.forEach(client => {
const position = client.position || { x: 0, y: 0 };
client.dragPosition = { x: position.x, y: position.y };
});
});
});
}
chunkArray(arr: any[], chunkSize: number): any[][] { chunkArray(arr: any[], chunkSize: number): any[][] {
const chunks = []; const chunks = [];
for (let i = 0; i < arr.length; i += chunkSize) { for (let i = 0; i < arr.length; i += chunkSize) {
@ -52,6 +72,40 @@ export class ClassroomViewComponent implements OnInit {
} }
handleClientClick(client: any): void { handleClientClick(client: any): void {
const dialogRef = this.dialog.open(ClientViewComponent, { data: { client }, width: '800px', height:'700px'}); const dialogRef = this.dialog.open(ClientViewComponent, { data: { client }, width: '800px', height:'700px' });
}
onDragMoved(event: CdkDragMove<any>, client: any): void {
const dragPosition = event.source.getFreeDragPosition()
client.position.x = dragPosition.x;
client.position.y = dragPosition.y;
}
saveDisposition(): void {
this.groupedClients.forEach(group => {
group.clientRows.forEach(row => {
row.forEach(client => {
const url = `http://127.0.0.1:8080/clients/${client.uuid}`;
const payload = {
name: client.name,
position: client.position
};
this.http.patch(url, payload).subscribe(response => {
console.log('Cliente actualizado:', response);
this.openSnackBar(false, 'Plano actualizado!');
}, error => {
console.error('Error al actualizar cliente:', error);
this.openSnackBar(true, error);
});
});
});
});
}
openSnackBar(isError: boolean, message: string) {
if (isError) {
this.toastService.error(' Error al actualizar cliente: ' + message, 'Error');
} else
this.toastService.success('Cliente actualizado!', 'Éxito');
} }
} }

View File

@ -121,3 +121,13 @@ mat-spinner {
margin: 0 auto; margin: 0 auto;
align-self: center; align-self: center;
} }
.container { /* Asegúrate de que esta clase sea la del contenedor del botón */
display: flex;
justify-content: flex-end;
}
.roomMap-btn {
}

View File

@ -4,7 +4,12 @@
<button mat-flat-button color="primary" (click)="addOU($event)">Nueva Unidad Organizativa</button> <button mat-flat-button color="primary" (click)="addOU($event)">Nueva Unidad Organizativa</button>
<button mat-flat-button color="primary" (click)="addClient($event)">Nuevo Cliente</button> <button mat-flat-button color="primary" (click)="addClient($event)">Nuevo Cliente</button>
<button mat-raised-button (click)="openBottomSheet()">Leyenda</button> <button mat-raised-button (click)="openBottomSheet()">Leyenda</button>
</div> </div>
<div class="container">
<button mat-flat-button class="roomMap-btn" color="accent" (click)="roomMap()" *ngIf="selectedDetail && selectedDetail.type === 'classroom'">Plano de aula</button>
</div>
</div> </div>
<div class="search-container"> <div class="search-container">
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
@ -142,5 +147,5 @@
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<app-classroom-view class="card classroom-view" [clients]="clientsData" [pcInTable]="5"></app-classroom-view> <!-- <app-classroom-view class="card classroom-view" [clients]="clientsData" [pcInTable]="5"></app-classroom-view> -->
</div> </div>

View File

@ -13,6 +13,8 @@ import {ToastrService} from "ngx-toastr";
import {TreeViewComponent} from "./tree-view/tree-view.component"; import {TreeViewComponent} from "./tree-view/tree-view.component";
import {MatBottomSheet} from "@angular/material/bottom-sheet"; import {MatBottomSheet} from "@angular/material/bottom-sheet";
import {LegendComponent} from "./legend/legend.component"; import {LegendComponent} from "./legend/legend.component";
import { ClassroomViewComponent } from './classroom-view/classroom-view.component';
import { ClassroomViewDialogComponent } from './classroom-view/classroom-view-modal';
@Component({ @Component({
selector: 'app-groups', selector: 'app-groups',
@ -251,4 +253,18 @@ constructor(
openBottomSheet(): void { openBottomSheet(): void {
this._bottomSheet.open(LegendComponent); this._bottomSheet.open(LegendComponent);
} }
roomMap(): void {
if (this.selectedDetail && this.selectedDetail.type === 'classroom') {
const dialogRef = this.dialog.open(ClassroomViewDialogComponent, {
width: '90vw',
height: '90vh',
data: { clients: this.clientsData }
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
});
}
}
} }