Primo rilascio
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<h2>Gestione Insegnanti</h2>
|
||||
|
||||
<div class="actions-header">
|
||||
<button mat-raised-button color="primary" (click)="addTeacher()">
|
||||
<mat-icon>person_add</mat-icon> Aggiungi Insegnante
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="spinner-container" *ngIf="isLoading">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<div *ngIf="error" class="error-message">
|
||||
{{ error }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="!isLoading && !error">
|
||||
<table mat-table [dataSource]="(teachers$ | async) ?? []" class="mat-elevation-z8 teacher-table">
|
||||
|
||||
<!-- Colonna ID -->
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef> ID </th>
|
||||
<td mat-cell *matCellDef="let teacher"> {{teacher.id}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Colonna Cognome -->
|
||||
<ng-container matColumnDef="last_name">
|
||||
<th mat-header-cell *matHeaderCellDef> Cognome </th>
|
||||
<td mat-cell *matCellDef="let teacher"> {{teacher.last_name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Colonna Nome -->
|
||||
<ng-container matColumnDef="first_name">
|
||||
<th mat-header-cell *matHeaderCellDef> Nome </th>
|
||||
<td mat-cell *matCellDef="let teacher"> {{teacher.first_name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Colonna Email -->
|
||||
<ng-container matColumnDef="email">
|
||||
<th mat-header-cell *matHeaderCellDef> Email </th>
|
||||
<td mat-cell *matCellDef="let teacher"> {{teacher.email || '-'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Colonna Telefono -->
|
||||
<ng-container matColumnDef="phone">
|
||||
<th mat-header-cell *matHeaderCellDef> Telefono </th>
|
||||
<td mat-cell *matCellDef="let teacher"> {{teacher.phone || '-'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Colonna Attivo -->
|
||||
<ng-container matColumnDef="is_active">
|
||||
<th mat-header-cell *matHeaderCellDef> Attivo </th>
|
||||
<td mat-cell *matCellDef="let teacher">
|
||||
<mat-slide-toggle
|
||||
[checked]="teacher.is_active"
|
||||
(change)="toggleActive(teacher, $event)"
|
||||
aria-label="Stato attivo insegnante">
|
||||
</mat-slide-toggle>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Colonna Azioni -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Azioni </th>
|
||||
<td mat-cell *matCellDef="let teacher">
|
||||
<button mat-icon-button color="primary" aria-label="Modifica insegnante" (click)="editTeacher(teacher)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" aria-label="Elimina insegnante" (click)="deleteTeacher(teacher)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
<!-- Riga da mostrare quando non ci sono dati -->
|
||||
<tr class="mat-row" *matNoDataRow>
|
||||
<td class="mat-cell" [attr.colspan]="displayedColumns.length">
|
||||
Nessun insegnante trovato.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
.actions-header {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.spinner-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: red;
|
||||
padding: 10px;
|
||||
border: 1px solid red;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.teacher-table {
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* Stile per la riga "Nessun dato" */
|
||||
.mat-row .mat-cell.mat-no-data-row {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
/* Spaziatura tra i pulsanti di azione */
|
||||
td.mat-cell button:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* Allineamento verticale per slide-toggle */
|
||||
td.mat-cell mat-slide-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%; /* Assicura che occupi l'altezza della cella */
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TeachersComponent } from './teachers.component';
|
||||
|
||||
describe('TeachersComponent', () => {
|
||||
let component: TeachersComponent;
|
||||
let fixture: ComponentFixture<TeachersComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TeachersComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TeachersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,155 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatSlideToggleModule, MatSlideToggleChange } from '@angular/material/slide-toggle'; // Importa anche MatSlideToggleChange
|
||||
import { FormsModule } from '@angular/forms'; // Necessario per ngModel in mat-slide-toggle
|
||||
import { TeacherService, Teacher, TeacherInput } from '../../services/teacher.service'; // Importa servizio e interfacce
|
||||
import { ConfirmDialogComponent, ConfirmDialogData } from '../confirm-dialog/confirm-dialog.component';
|
||||
import { TeacherDialogComponent } from '../teacher-dialog/teacher-dialog.component'; // Importa il dialog insegnanti
|
||||
// import { TeacherDialogComponent } from '../teacher-dialog/teacher-dialog.component';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-teachers',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule, // Aggiungi FormsModule
|
||||
MatTableModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatDialogModule,
|
||||
MatSlideToggleModule // Aggiungi MatSlideToggleModule
|
||||
],
|
||||
templateUrl: './teachers.component.html',
|
||||
styleUrl: './teachers.component.scss'
|
||||
})
|
||||
export class TeachersComponent implements OnInit {
|
||||
|
||||
teachers$: Observable<Teacher[]> | undefined;
|
||||
// Colonne per la tabella insegnanti (adattate)
|
||||
displayedColumns: string[] = ['id', 'last_name', 'first_name', 'email', 'phone', 'is_active', 'actions'];
|
||||
isLoading = false;
|
||||
error: string | null = null;
|
||||
|
||||
constructor(
|
||||
private teacherService: TeacherService,
|
||||
private dialog: MatDialog
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadTeachers();
|
||||
}
|
||||
|
||||
loadTeachers(): void {
|
||||
this.isLoading = true;
|
||||
this.error = null;
|
||||
this.teachers$ = this.teacherService.getTeachers();
|
||||
|
||||
this.teachers$.subscribe({
|
||||
next: () => this.isLoading = false,
|
||||
error: (err) => {
|
||||
console.error('Error loading teachers:', err);
|
||||
this.error = 'Errore durante il caricamento degli insegnanti.';
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addTeacher(): void {
|
||||
const dialogRef = this.dialog.open(TeacherDialogComponent, {
|
||||
width: '600px', // Larghezza maggiore per più campi
|
||||
data: {} // Dati vuoti per la modalità aggiunta
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.isLoading = true;
|
||||
this.teacherService.addTeacher(result).subscribe({
|
||||
next: () => this.loadTeachers(),
|
||||
error: (err) => {
|
||||
console.error('Error adding teacher:', err);
|
||||
this.error = 'Errore durante l\'aggiunta dell\'insegnante.';
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
editTeacher(teacher: Teacher): void {
|
||||
// Recupera i dati completi dell'insegnante prima di aprire il dialog
|
||||
// perché la tabella potrebbe mostrare solo dati parziali
|
||||
this.teacherService.getTeacher(teacher.id).subscribe({
|
||||
next: (fullTeacherData) => {
|
||||
const dialogRef = this.dialog.open(TeacherDialogComponent, {
|
||||
width: '600px',
|
||||
data: { teacher: fullTeacherData } // Passa i dati completi
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.isLoading = true;
|
||||
this.teacherService.updateTeacher(teacher.id, result).subscribe({
|
||||
next: () => this.loadTeachers(),
|
||||
error: (err) => {
|
||||
console.error(`Error updating teacher ${teacher.id}:`, err);
|
||||
this.error = 'Errore durante la modifica dell\'insegnante.';
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(`Error fetching full teacher data for ID ${teacher.id}:`, err);
|
||||
this.error = 'Errore nel recuperare i dati completi per la modifica.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteTeacher(teacher: Teacher): void {
|
||||
const dialogData: ConfirmDialogData = {
|
||||
title: 'Conferma Eliminazione Insegnante',
|
||||
message: `Sei sicuro di voler eliminare l'insegnante "${teacher.first_name} ${teacher.last_name}" (ID: ${teacher.id})?`,
|
||||
confirmButtonText: 'Elimina'
|
||||
};
|
||||
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '400px',
|
||||
data: dialogData
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(confirmed => {
|
||||
if (confirmed) {
|
||||
this.isLoading = true;
|
||||
this.teacherService.deleteTeacher(teacher.id).subscribe({
|
||||
next: () => this.loadTeachers(),
|
||||
error: (err) => {
|
||||
console.error(`Error deleting teacher ${teacher.id}:`, err);
|
||||
this.error = 'Errore durante l\'eliminazione dell\'insegnante.';
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Gestisce il cambio di stato attivo/inattivo direttamente dalla tabella
|
||||
toggleActive(teacher: Teacher, event: MatSlideToggleChange): void {
|
||||
// event.stopPropagation(); // Non necessario/possibile con (change) di MatSlideToggleChange
|
||||
const updatedTeacher: TeacherInput = { is_active: !teacher.is_active };
|
||||
this.teacherService.updateTeacher(teacher.id, updatedTeacher).subscribe({
|
||||
next: () => this.loadTeachers(), // Ricarica per vedere lo stato aggiornato
|
||||
error: (err) => {
|
||||
console.error(`Error toggling active state for teacher ${teacher.id}:`, err);
|
||||
this.error = 'Errore durante l\'aggiornamento dello stato.';
|
||||
// Potrebbe essere utile ripristinare lo stato visivo del toggle in caso di errore
|
||||
this.loadTeachers();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user