refs #729. RemoteCalendar and RemoteCalendarRule. First commit, 90%
parent
99489d54a4
commit
9241a80be7
|
@ -16,6 +16,7 @@ import {OgbootStatusComponent} from "./components/ogboot/ogboot-status/ogboot-st
|
|||
import { OgdhcpComponent } from './components/ogdhcp/ogdhcp.component';
|
||||
import { OgDhcpSubnetsComponent } from './components/ogdhcp/og-dhcp-subnets/og-dhcp-subnets.component';
|
||||
import { CommandsComponent } from './components/commands/commands.component';
|
||||
import { CalendarComponent } from "./components/calendar/calendar.component";
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
||||
{
|
||||
|
@ -34,6 +35,7 @@ const routes: Routes = [
|
|||
{ path: 'dhcp', component: OgdhcpComponent },
|
||||
{ path: 'dhcp-subnets', component: OgDhcpSubnetsComponent },
|
||||
{ path: 'commands', component: CommandsComponent },
|
||||
{ path: 'calendars', component: CalendarComponent },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -88,6 +88,10 @@ import { CommandDetailComponent } from './components/commands/command-detail/com
|
|||
import { CreateCommandComponent } from './components/commands/create-command/create-command.component';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatNativeDateModule } from '@angular/material/core';
|
||||
import { CalendarComponent } from './components/calendar/calendar.component';
|
||||
import { CreateCalendarComponent } from './components/calendar/create-calendar/create-calendar.component';
|
||||
import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
|
||||
import { CreateCalendarRuleComponent } from './components/calendar/create-calendar-rule/create-calendar-rule.component';
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
@ -132,7 +136,10 @@ import { MatNativeDateModule } from '@angular/material/core';
|
|||
AddClientsToSubnetComponent,
|
||||
CommandsComponent,
|
||||
CommandDetailComponent,
|
||||
CreateCommandComponent
|
||||
CreateCommandComponent,
|
||||
CalendarComponent,
|
||||
CreateCalendarComponent,
|
||||
CreateCalendarRuleComponent
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [BrowserModule,
|
||||
|
@ -169,7 +176,7 @@ import { MatNativeDateModule } from '@angular/material/core';
|
|||
progressAnimation: 'increasing',
|
||||
closeButton: true
|
||||
}
|
||||
), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile, MatExpansionPanel, MatExpansionPanelTitle, MatExpansionPanelDescription
|
||||
), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile, MatExpansionPanel, MatExpansionPanelTitle, MatExpansionPanelDescription, MatRadioGroup, MatRadioButton
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA,
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
.title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.images-button-row {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.lists-container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.imagesLists-container {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card.unidad-card {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid rgba(122, 122, 122, 0.555);
|
||||
}
|
||||
|
||||
.image-container h4 {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.image-name{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-string {
|
||||
flex: 2;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.search-boolean {
|
||||
flex: 1;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mat-elevation-z8 {
|
||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.paginator-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-expansion-panel-header-description {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.example-button-row {
|
||||
display: table-cell;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.example-button-row .mat-mdc-button-base {
|
||||
margin: 8px 8px 8px 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<div class="header-container">
|
||||
<h2 class="title" i18n="@@adminImagesTitle">Administrar calendarios</h2>
|
||||
<div class="images-button-row">
|
||||
<button mat-flat-button color="primary" (click)="addImage()">Añadir calendario</button>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
<div class="search-container">
|
||||
<mat-form-field appearance="fill" class="search-string">
|
||||
<mat-label i18n="@@searchLabel">Buscar nombre de calendario</mat-label>
|
||||
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
|
||||
<mat-icon matSuffix>search</mat-icon>
|
||||
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image" >
|
||||
<ng-container *ngIf="column.columnDef === 'isDefault' || column.columnDef === 'installed'">
|
||||
<mat-icon [color]="image[column.columnDef] ? 'primary' : 'warn'">
|
||||
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
|
||||
</mat-icon>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="column.columnDef === 'downloadUrl'">
|
||||
<span matTooltip="{{ image.downloadUrl }}">
|
||||
{{ image.downloadUrl ? image.downloadUrl.substring(0, 20) + '...' : '' }}
|
||||
</span>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="column.columnDef !== 'isDefault' && column.columnDef !== 'installed' && column.columnDef !== 'downloadUrl'">
|
||||
{{ column.cell(image) }}
|
||||
</ng-container>
|
||||
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
|
||||
<td mat-cell *matCellDef="let calendar" style="text-align: center;">
|
||||
<button mat-icon-button color="primary" (click)="editImage(calendar)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
|
||||
<button mat-icon-button color="primary" (click)="sync(calendar)"><mat-icon>sync</mat-icon></button>
|
||||
<button mat-icon-button color="warn" (click)="deleteCalendar(calendar)" i18n="@@buttonDelete"><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>
|
||||
</table>
|
||||
<div class="paginator-container">
|
||||
<mat-paginator [length]="length"
|
||||
[pageSize]="itemsPerPage"
|
||||
[pageIndex]="page"
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
(page)="onPageChange($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CalendarComponent } from './calendar.component';
|
||||
|
||||
describe('CalendarComponent', () => {
|
||||
let component: CalendarComponent;
|
||||
let fixture: ComponentFixture<CalendarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [CalendarComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CalendarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,149 @@
|
|||
import {Component, OnInit, signal} from '@angular/core';
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {DatePipe} from "@angular/common";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {DataService} from "./data.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {InfoImageComponent} from "../ogboot/images/info-image/info-image/info-image.component";
|
||||
import {PageEvent} from "@angular/material/paginator";
|
||||
import {CreateCalendarComponent} from "./create-calendar/create-calendar.component";
|
||||
import {DeleteModalComponent} from "../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-calendar',
|
||||
templateUrl: './calendar.component.html',
|
||||
styleUrl: './calendar.component.css'
|
||||
})
|
||||
export class CalendarComponent implements OnInit {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
images: { downloadUrl: string; name: string; uuid: string }[] = [];
|
||||
dataSource = new MatTableDataSource<any>();
|
||||
length: number = 0;
|
||||
itemsPerPage: number = 10;
|
||||
page: number = 1;
|
||||
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
|
||||
selectedElements: string[] = [];
|
||||
loading:boolean = false;
|
||||
filters: { [key: string]: string } = {};
|
||||
alertMessage: string | null = null;
|
||||
readonly panelOpenState = signal(false);
|
||||
datePipe: DatePipe = new DatePipe('es-ES');
|
||||
columns = [
|
||||
{
|
||||
columnDef: 'id',
|
||||
header: 'ID',
|
||||
cell: (user: any) => `${user.id}`,
|
||||
},
|
||||
{
|
||||
columnDef: 'name',
|
||||
header: 'Nombre',
|
||||
cell: (user: any) => `${user.name}`
|
||||
},
|
||||
{
|
||||
columnDef: 'createdAt',
|
||||
header: 'Fecha de creación',
|
||||
cell: (user: any) => `${this.datePipe.transform(user.createdAt, 'dd/MM/yyyy hh:mm:ss')}`,
|
||||
}
|
||||
];
|
||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||
|
||||
private apiUrl = `${this.baseUrl}/remote-calendars`;
|
||||
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private http: HttpClient,
|
||||
private dataService: DataService,
|
||||
private toastService: ToastrService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.search();
|
||||
}
|
||||
|
||||
addImage(): void {
|
||||
const dialogRef = this.dialog.open(CreateCalendarComponent, {
|
||||
width: '400px'
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
console.log('The dialog was closed');
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
|
||||
search(): void {
|
||||
this.loading = true;
|
||||
this.dataService.getRemoteCalendars(this.filters).subscribe(
|
||||
data => {
|
||||
this.dataSource.data = data;
|
||||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
console.error('Error fetching og lives', error);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sync(calendar: any): void {
|
||||
console.log('Syncing calendars');
|
||||
}
|
||||
|
||||
editImage(calendar: any): void {
|
||||
const dialogRef = this.dialog.open(CreateCalendarComponent, {
|
||||
width: '700px',
|
||||
data: calendar
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.search();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteCalendar(role: any): void {
|
||||
const dialogRef = this.dialog.open(DeleteModalComponent, {
|
||||
width: '300px',
|
||||
data: { name: role.name }
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
const apiUrl = `${this.baseUrl}/remote-calendars/${role.uuid}`;
|
||||
|
||||
this.http.delete(apiUrl).subscribe({
|
||||
next: () => {
|
||||
console.log('Role deleted successfully');
|
||||
this.search();
|
||||
this.toastService.success('Role deleted successfully');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error deleting role:', error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('Role deletion cancelled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||
next: (response) => {
|
||||
this.dataSource.data = response['hydra:member'];
|
||||
this.length = response['hydra:totalItems'];
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error al cargar las imágenes:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPageChange(event: PageEvent) {
|
||||
this.page = event.pageIndex;
|
||||
this.itemsPerPage = event.pageSize;
|
||||
this.applyFilter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.form-container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 15px 0;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.time-fields {
|
||||
display: flex;
|
||||
gap: 15px; /* Espacio entre los campos */
|
||||
}
|
||||
|
||||
.time-field {
|
||||
flex: 1;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} calendario</h2>
|
||||
<mat-dialog-content class="form-container">
|
||||
<mat-slide-toggle [(ngModel)]="isRemoteAvailable" class="example-margin">¿Disponibilidad remoto?</mat-slide-toggle>
|
||||
|
||||
<div *ngIf="!isRemoteAvailable" class="form-group">
|
||||
<mat-label>Selecciona los días de la semana</mat-label>
|
||||
<div class="checkbox-group">
|
||||
<mat-checkbox *ngFor="let day of weekDays" [(ngModel)]="busyWeekDays[day]">
|
||||
{{ day }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="time-fields">
|
||||
<mat-form-field appearance="fill" class="time-field">
|
||||
<mat-label>Hora de inicio</mat-label>
|
||||
<input matInput [(ngModel)]="busyFromHour" type="time" placeholder="Selecciona la hora de inicio">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill" class="time-field">
|
||||
<mat-label>Hora de fin</mat-label>
|
||||
<input matInput [(ngModel)]="busyToHour" type="time" placeholder="Selecciona la hora de fin">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isRemoteAvailable" class="form-group">
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Razón</mat-label>
|
||||
<input matInput [(ngModel)]="availableReason" placeholder="Razon para la excepción">
|
||||
</mat-form-field>
|
||||
<div class="time-fields">
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Fecha de inicio</mat-label>
|
||||
<input matInput [matDatepicker]="picker1" [(ngModel)]="availableFromDate">
|
||||
<mat-hint>MM/DD/YYYY</mat-hint>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="picker1"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker1></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Fecha de fin</mat-label>
|
||||
<input matInput [matDatepicker]="picker2" [(ngModel)]="availableToDate">
|
||||
<mat-hint>MM/DD/YYYY</mat-hint>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="picker2"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker2></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="onNoClick()">Cancelar</button>
|
||||
<button mat-button (click)="submitRule()" cdkFocusInitial>{{ isEditMode ? 'Guardar' : 'Añadir' }}</button>
|
||||
</mat-dialog-actions>
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CreateCalendarRuleComponent } from './create-calendar-rule.component';
|
||||
|
||||
describe('CreateCalendarRuleComponent', () => {
|
||||
let component: CreateCalendarRuleComponent;
|
||||
let fixture: ComponentFixture<CreateCalendarRuleComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [CreateCalendarRuleComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CreateCalendarRuleComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,115 @@
|
|||
import {Component, Inject} from '@angular/core';
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-calendar-rule',
|
||||
templateUrl: './create-calendar-rule.component.html',
|
||||
styleUrl: './create-calendar-rule.component.css'
|
||||
})
|
||||
export class CreateCalendarRuleComponent {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
name: string = '';
|
||||
remoteCalendarRules: any[] = [];
|
||||
weekDays: string[] = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'];
|
||||
busyWeekDays: { [key: string]: boolean } = {};
|
||||
busyFromHour: any = null;
|
||||
busyToHour: any = null;
|
||||
availableFromDate: any = null;
|
||||
availableToDate: any = null;
|
||||
isRemoteAvailable: boolean = false;
|
||||
showAdditionalForm: boolean = false;
|
||||
availableReason: any = null;
|
||||
isEditMode: boolean = false;
|
||||
ruleId: string | null = null;
|
||||
calendarId: string | null = null;
|
||||
selectedDaysIndices: number[] = [];
|
||||
|
||||
constructor(
|
||||
private toastService: ToastrService,
|
||||
private http: HttpClient,
|
||||
public dialogRef: MatDialogRef<CreateCalendarRuleComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.calendarId = this.data.calendar
|
||||
if (this.data) {
|
||||
this.isEditMode = true;
|
||||
this.availableFromDate = this.data.rule.availableFromDate;
|
||||
this.availableToDate = this.data.rule.availableToDate;
|
||||
this.isRemoteAvailable = this.data.rule.isRemoteAvailable;
|
||||
this.availableReason = this.data.rule.availableReason;
|
||||
this.busyFromHour = this.data.rule.busyFromHour;
|
||||
this.busyToHour = this.data.rule.busyToHour;
|
||||
if (this.data.rule.busyWeekDays) {
|
||||
this.busyWeekDays = this.data.rule.busyWeekDays.reduce((acc: {
|
||||
[x: string]: boolean;
|
||||
}, day: string | number) => {
|
||||
// @ts-ignore
|
||||
acc[this.weekDays[day]] = true;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
this.ruleId = this.data.rule['@id']
|
||||
}
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
toggleAdditionalForm(): void {
|
||||
this.showAdditionalForm = !this.showAdditionalForm;
|
||||
}
|
||||
|
||||
getSelectedDaysIndices() {
|
||||
this.selectedDaysIndices = this.weekDays
|
||||
.map((day, index) => this.busyWeekDays[day] ? index : -1)
|
||||
.filter(index => index !== -1);
|
||||
}
|
||||
|
||||
submitRule(): void {
|
||||
this.getSelectedDaysIndices()
|
||||
const selectedDaysArray = Object.keys(this.busyWeekDays).map((day, index) => this.busyWeekDays[index]);
|
||||
|
||||
const formData = {
|
||||
remoteCalendar: this.calendarId,
|
||||
busyWeekDays: this.selectedDaysIndices,
|
||||
busyFromHour: this.busyFromHour,
|
||||
busyToHour: this.busyFromHour,
|
||||
availableFromDate: this.availableFromDate,
|
||||
availableToDate: this.availableToDate,
|
||||
isRemoteAvailable: this.isRemoteAvailable,
|
||||
availableReason: this.availableReason
|
||||
};
|
||||
|
||||
if (this.isEditMode && this.ruleId) {
|
||||
this.http.put(`${this.baseUrl}${this.ruleId}`, formData)
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
this.toastService.success('Calendar updated successfully');
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error:', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.http.post(`${this.baseUrl}/remote-calendar-rules`, formData)
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
this.toastService.success('Calendar created successfully');
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error:', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log(formData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.form-container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.additional-form {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 15px 0;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.time-fields {
|
||||
display: flex;
|
||||
gap: 15px; /* Espacio entre los campos */
|
||||
}
|
||||
|
||||
.time-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.list-item-content {
|
||||
display: flex;
|
||||
align-items: flex-start; /* Alinea el contenido al inicio */
|
||||
justify-content: space-between; /* Espacio entre los textos y los íconos */
|
||||
width: 100%; /* Asegúrate de que el contenido ocupe todo el ancho */
|
||||
}
|
||||
|
||||
.text-content {
|
||||
flex-grow: 1; /* Permite que este contenedor ocupe el espacio disponible */
|
||||
margin-right: 16px; /* Espaciado a la derecha para separar de los íconos */
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: flex;
|
||||
align-items: center; /* Alinea los íconos verticalmente */
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
margin-left: 8px; /* Espaciado entre los íconos */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} calendario</h2>
|
||||
<mat-dialog-content class="form-container">
|
||||
<button mat-flat-button color="primary" *ngIf="isEditMode" (click)="createRule()" style="padding: 10px;">Añadir regla</button>
|
||||
|
||||
<!-- Campo para el nombre -->
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Nombre</mat-label>
|
||||
<input matInput [(ngModel)]="name">
|
||||
</mat-form-field>
|
||||
|
||||
<div mat-subheader>Reglas</div>
|
||||
<mat-list>
|
||||
<ng-container *ngFor="let rule of remoteCalendarRules;">
|
||||
<mat-list-item>
|
||||
<div class="list-item-content">
|
||||
<mat-icon matListItemIcon>event_available</mat-icon>
|
||||
<div class="text-content">
|
||||
<div matListItemTitle>{{ rule.isRemoteAvailable ? 'Disponible' : 'No disponible ( periodo presencial )' }}</div>
|
||||
<div matListItemLine *ngIf="!rule.isRemoteAvailable">{{ rule.busyFromHour }} - {{ rule.busyToHour }}</div>
|
||||
<div matListItemLine *ngIf="rule.isRemoteAvailable">{{ rule.availableReason }} | {{ rule.busyFromHour }} - {{ rule.busyToHour }}</div>
|
||||
</div>
|
||||
<div class="icon-container">
|
||||
<button mat-icon-button color="primary" class="right-icon" (click)="createRule(rule)" i18n="@@editImage">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" class="right-icon" >
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-list-item>
|
||||
</ng-container>
|
||||
</mat-list>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="onNoClick()">Cancelar</button>
|
||||
<button mat-button (click)="submitForm()" cdkFocusInitial>{{ isEditMode ? 'Guardar' : 'Añadir' }}</button>
|
||||
</mat-dialog-actions>
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CreateCalendarComponent } from './create-calendar.component';
|
||||
|
||||
describe('CreateCalendarComponent', () => {
|
||||
let component: CreateCalendarComponent;
|
||||
let fixture: ComponentFixture<CreateCalendarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [CreateCalendarComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CreateCalendarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,86 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog";
|
||||
import {CreateCalendarRuleComponent} from "../create-calendar-rule/create-calendar-rule.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-calendar',
|
||||
templateUrl: './create-calendar.component.html',
|
||||
styleUrl: './create-calendar.component.css'
|
||||
})
|
||||
export class CreateCalendarComponent implements OnInit {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
name: string = '';
|
||||
remoteCalendarRules: any[] = [];
|
||||
isEditMode: boolean = false;
|
||||
calendarId: string | null = null;
|
||||
|
||||
constructor(
|
||||
private toastService: ToastrService,
|
||||
private http: HttpClient,
|
||||
public dialogRef: MatDialogRef<CreateCalendarComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
public dialog: MatDialog,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.data) {
|
||||
this.isEditMode = true;
|
||||
this.name = this.data.name;
|
||||
this.remoteCalendarRules = this.data.remoteCalendarRules;
|
||||
this.calendarId = this.data['@id']
|
||||
}
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
const payload = {
|
||||
name: this.name
|
||||
};
|
||||
|
||||
if (this.isEditMode && this.calendarId) {
|
||||
this.http.patch(`${this.baseUrl}${this.calendarId}`, payload)
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
this.toastService.success('Calendar updated successfully');
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error:', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.http.post(`${this.baseUrl}/remote-calendars`, payload)
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
this.toastService.success('Calendar created successfully');
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error:', error);
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createRule(rule: any = null): void {
|
||||
const dialogRef = this.dialog.open(CreateCalendarRuleComponent, {
|
||||
width: '700px',
|
||||
data: {
|
||||
calendar: this.calendarId,
|
||||
rule: rule
|
||||
},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DataService {
|
||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||
private apiUrl = `${this.baseUrl}/remote-calendars?page=1&itemsPerPage=1000`;
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getRemoteCalendars(filters: { [key: string]: string }): Observable<any[]> {
|
||||
const params = new HttpParams({ fromObject: filters });
|
||||
|
||||
return this.http.get<any>(this.apiUrl, { params }).pipe(
|
||||
map(response => {
|
||||
if (response['hydra:member'] && Array.isArray(response['hydra:member'])) {
|
||||
return response['hydra:member'];
|
||||
} else {
|
||||
throw new Error('Unexpected response format');
|
||||
}
|
||||
}),
|
||||
catchError(error => {
|
||||
console.error('Error fetching remote calendars', error);
|
||||
return throwError(error);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -104,7 +104,7 @@
|
|||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-list-item>
|
||||
<mat-list-item routerLink="/calendars">
|
||||
<span class="entry">
|
||||
<mat-icon class="icon">calendar_month</mat-icon>
|
||||
<span i18n="@@calendars">Calendarios</span>
|
||||
|
|
Loading…
Reference in New Issue