215 lines
8.0 KiB
TypeScript
215 lines
8.0 KiB
TypeScript
import { Component, OnInit, signal, computed, inject } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { Router, RouterModule } from '@angular/router';
|
|
|
|
export interface Resource {
|
|
id: string;
|
|
name: string;
|
|
type: 'Sala Reuniones' | 'Oficina Flexible' | 'Zona Relax' | 'Evento';
|
|
icon: string;
|
|
desc: string;
|
|
capacity: number;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'app-calendar-booking',
|
|
standalone: true,
|
|
imports: [CommonModule, RouterModule],
|
|
templateUrl: './calendar-booking.component.html',
|
|
styleUrls: ['./calendar-booking.component.css']
|
|
})
|
|
export class CalendarBookingComponent implements OnInit {
|
|
private router = inject(Router);
|
|
|
|
// Horarios de la captura
|
|
timeSlots = ['08:00', '09:30', '10:00', '11:30', '12:00', '13:30', '14:00', '15:00'];
|
|
|
|
// Base de datos de recursos por categoría
|
|
resources = signal<Resource[]>([
|
|
{ id: '1', name: 'SALA LUNA', type: 'Sala Reuniones', icon: '🌙', desc: 'Sala Reuniones resutenias or Sala Luna', capacity: 8 },
|
|
{ id: '2', name: 'SALA SOL', type: 'Sala Reuniones', icon: '☀️', desc: 'Estables:nación de nuevos luxes', capacity: 12 },
|
|
{ id: '3', name: 'OFICINA FLEX', type: 'Oficina Flexible', icon: '🏢', desc: 'Resultalioes sala tia r Oficina flexible', capacity: 1 },
|
|
{ id: '4', name: 'OFICINA FLEX A1', type: 'Oficina Flexible', icon: '🏢', desc: 'Oficina de espacios resutentu-u planetia', capacity: 1 },
|
|
{ id: '5', name: 'ZONA COFFEE', type: 'Zona Relax', icon: '☕', desc: 'Espacio de café y socialización', capacity: 20 },
|
|
{ id: '6', name: 'SALA DE JUEGOS', type: 'Zona Relax', icon: '🎮', desc: 'Mesa de ping pong y consolas', capacity: 10 },
|
|
{ id: '7', name: 'AUDITORIO A', type: 'Evento', icon: '🎤', desc: 'Auditorio principal para presentaciones', capacity: 100 },
|
|
{ id: '8', name: 'TERRAZA', type: 'Evento', icon: '🌿', desc: 'Espacio abierto para eventos corporativos', capacity: 50 }
|
|
]);
|
|
|
|
// Signals para gestionar estado interactivo
|
|
selectedCategories = signal<string[]>(['Sala Reuniones', 'Oficina Flexible', 'Zona Relax', 'Evento']);
|
|
searchQuery = signal<string>('');
|
|
|
|
// Slot seleccionado para el popup dinámico
|
|
selectedSlot = signal<{ resource: Resource, time: string } | null>(null);
|
|
|
|
// Ocupación en tiempo real (inicializada con la captura)
|
|
reservations = signal<any[]>([
|
|
{ resourceName: 'SALA LUNA', time: '08:00', author: 'Sistema', date: 'Hace 10h' },
|
|
{ resourceName: 'SALA LUNA', time: '09:30', author: 'Sistema', date: 'Hace 10h' },
|
|
{ resourceName: 'SALA LUNA', time: '13:30', author: 'Sistema', date: 'Hace 10h' },
|
|
{ resourceName: 'SALA LUNA', time: '14:00', author: 'Sistema', date: 'Hace 10h' },
|
|
{ resourceName: 'SALA SOL', time: '08:00', author: 'Sistema', date: 'Hace 10h' },
|
|
{ resourceName: 'SALA SOL', time: '09:30', author: 'Sistema', date: 'Hace 10h' }
|
|
]);
|
|
|
|
// Notificaciones estáticas de David Chen
|
|
staticNotifications = [
|
|
{ title: 'David Chen', detail: 'compartió Resultados del Trimestre', time: 'Hace 15m', type: 'notif', avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=120&q=80' },
|
|
{ title: 'David Chen', detail: 'compartió Resultados del Trimestre', time: 'Hace 15m', type: 'notif', avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=120&q=80' }
|
|
];
|
|
|
|
// Mini calendar data for June 2026
|
|
miniCalendarDays = [
|
|
{ day: 31, currentMonth: false },
|
|
{ day: 1, currentMonth: true },
|
|
{ day: 2, currentMonth: true },
|
|
{ day: 3, currentMonth: true },
|
|
{ day: 4, currentMonth: true },
|
|
{ day: 5, currentMonth: true },
|
|
{ day: 6, currentMonth: true },
|
|
{ day: 7, currentMonth: true },
|
|
{ day: 8, currentMonth: true },
|
|
{ day: 9, currentMonth: true },
|
|
{ day: 10, currentMonth: true },
|
|
{ day: 11, currentMonth: true },
|
|
{ day: 12, currentMonth: true },
|
|
{ day: 13, currentMonth: true },
|
|
{ day: 14, currentMonth: true },
|
|
{ day: 15, currentMonth: true },
|
|
{ day: 16, currentMonth: true, active: true },
|
|
{ day: 17, currentMonth: true },
|
|
{ day: 18, currentMonth: true },
|
|
{ day: 19, currentMonth: true },
|
|
{ day: 20, currentMonth: true },
|
|
{ day: 21, currentMonth: true },
|
|
{ day: 22, currentMonth: true },
|
|
{ day: 23, currentMonth: true },
|
|
{ day: 24, currentMonth: true },
|
|
{ day: 25, currentMonth: true },
|
|
{ day: 26, currentMonth: true },
|
|
{ day: 27, currentMonth: true },
|
|
{ day: 28, currentMonth: true },
|
|
{ day: 29, currentMonth: true },
|
|
{ day: 30, currentMonth: true },
|
|
{ day: 1, currentMonth: false },
|
|
{ day: 2, currentMonth: false },
|
|
{ day: 3, currentMonth: false },
|
|
{ day: 4, currentMonth: false },
|
|
{ day: 5, currentMonth: false },
|
|
{ day: 6, currentMonth: false },
|
|
{ day: 7, currentMonth: false },
|
|
{ day: 8, currentMonth: false },
|
|
{ day: 9, currentMonth: false },
|
|
{ day: 10, currentMonth: false },
|
|
{ day: 11, currentMonth: false }
|
|
];
|
|
|
|
// Computed signals
|
|
filteredResources = computed(() => {
|
|
const selectedCats = this.selectedCategories();
|
|
const query = this.searchQuery().toLowerCase().trim();
|
|
return this.resources().filter(r =>
|
|
selectedCats.includes(r.type) &&
|
|
(!query || r.name.toLowerCase().includes(query) || r.desc.toLowerCase().includes(query))
|
|
);
|
|
});
|
|
|
|
recentActivities = computed(() => {
|
|
// Convertir reservas a actividades
|
|
const resList = this.reservations().map(res => {
|
|
let avatar = 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&w=120&q=80';
|
|
if (res.resourceName.includes('LUNA')) {
|
|
avatar = 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=120&q=80';
|
|
}
|
|
return {
|
|
title: res.resourceName,
|
|
detail: `Reservado ${res.time}`,
|
|
time: res.date,
|
|
type: 'reserva',
|
|
avatar: avatar
|
|
};
|
|
});
|
|
return [...resList, ...this.staticNotifications];
|
|
});
|
|
|
|
ngOnInit(): void {
|
|
// Inicializar el popup en Sala Luna de las 10:00 para emular la captura al cargar
|
|
const luna = this.resources().find(r => r.name === 'SALA LUNA');
|
|
if (luna) {
|
|
this.selectedSlot.set({ resource: luna, time: '11:30' });
|
|
}
|
|
}
|
|
|
|
toggleCategory(cat: string): void {
|
|
this.selectedCategories.update(prev => {
|
|
if (prev.includes(cat)) {
|
|
return prev.filter(c => c !== cat);
|
|
} else {
|
|
return [...prev, cat];
|
|
}
|
|
});
|
|
this.selectedSlot.set(null); // Reset popup al cambiar filtros
|
|
}
|
|
|
|
isCategorySelected(cat: string): boolean {
|
|
return this.selectedCategories().includes(cat);
|
|
}
|
|
|
|
onSearch(event: Event): void {
|
|
const value = (event.target as HTMLInputElement).value;
|
|
this.searchQuery.set(value);
|
|
}
|
|
|
|
isSlotOccupied(resourceName: string, time: string): boolean {
|
|
return this.reservations().some(res => res.resourceName === resourceName && res.time === time);
|
|
}
|
|
|
|
onSlotClick(resource: Resource, time: string): void {
|
|
if (this.isSlotOccupied(resource.name, time)) {
|
|
return;
|
|
}
|
|
this.selectedSlot.set({ resource, time });
|
|
}
|
|
|
|
getEndTime(startTime: string): string {
|
|
const [h, m] = startTime.split(':').map(Number);
|
|
let fh = h + 1;
|
|
let fm = m + 30;
|
|
if (fm >= 60) {
|
|
fh += 1;
|
|
fm -= 60;
|
|
}
|
|
return `${String(fh).padStart(2, '0')}:${String(fm).padStart(2, '0')}`;
|
|
}
|
|
|
|
confirmReserva(): void {
|
|
const slot = this.selectedSlot();
|
|
if (slot) {
|
|
const newRes = {
|
|
resourceName: slot.resource.name,
|
|
time: slot.time,
|
|
author: 'Jess Miller',
|
|
date: 'Hace un momento'
|
|
};
|
|
this.reservations.update(prev => [newRes, ...prev]);
|
|
this.selectedSlot.set(null);
|
|
}
|
|
}
|
|
|
|
closePopup(): void {
|
|
this.selectedSlot.set(null);
|
|
}
|
|
|
|
navigateToNuevo(): void {
|
|
const slot = this.selectedSlot();
|
|
this.router.navigate(['/reservas/nuevo'], {
|
|
queryParams: {
|
|
fecha: new Date().toISOString().split('T')[0],
|
|
tipo: slot ? (slot.resource.type === 'Sala Reuniones' ? 'room' : 'desk') : 'room',
|
|
hora: slot ? slot.time : '10:00'
|
|
}
|
|
});
|
|
}
|
|
}
|