npm package: @nuvaprd/production-calendar
Description: Web Component de calendario de producción compatible con Angular 8+
Author: Nuva
Latest version: 1.2.1

# 📅 @nuvaprd/production-calendar

<p align="center">
  <img src="https://avatars.githubusercontent.com/u/135178402?v=4" width="120" />
</p>

> Web Component moderno para gestión de calendario de producción, construido con Lit y totalmente compatible con Angular 8+

[![npm version](https://img.shields.io/npm/v/@nuvaprd/production-calendar.svg)](https://www.npmjs.com/package/@nuvaprd/production-calendar)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)

## ✨ Características

- 🎯 **Compatible con Angular 8+** - TypeScript 3.4+ soportado
- 🔄 **Migración fácil** desde angular-calendar - API similar
- 📱 **3 Vistas**: Mes, Semana, Día con navegación intuitiva
- 🎨 **Colores personalizables** por evento
- 📊 **Panel lateral dinámico** con vista Lista y Gantt
- ⏰ **Control de visualización de horas** - Ocultar/mostrar horas de eventos según necesidad
- 🚀 **Rendimiento optimizado** con Shadow DOM
- 🎭 **Framework agnostic** - Funciona con React, Vue, vanilla JS
- 🌍 **i18n ready** - Soporte multiidioma
- ♿ **Accesible** - ARIA labels y navegación por teclado

---

## 📦 Instalación

```bash
npm install @nuvaprd/production-calendar
```

---

## 🚀 Integración con Angular 8+

### 1. Configurar el módulo

Agrega `CUSTOM_ELEMENTS_SCHEMA` en tu módulo para permitir Web Components:

```typescript
// app-schedule.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@NgModule({
  declarations: [ScheduleListComponent],
  imports: [CommonModule, FormsModule],
  schemas: [CUSTOM_ELEMENTS_SCHEMA] // ← Importante para Web Components
})
export class AppScheduleModule { }
```

### 2. Agregar script en angular.json

Agrega el archivo UMD en los scripts para carga global:

```json
{
  "architect": {
    "build": {
      "options": {
        "scripts": [
          "node_modules/@nuvaprd/production-calendar/dist/production-calendar.umd.js"
        ]
      }
    }
  }
}
```

### 3. Definir tipos en el componente

Dado que Angular 8 usa TypeScript 3.4 (sin soporte para `import type`), define los tipos localmente:

```typescript
// schedule-list.component.ts
import { Component, OnInit } from '@angular/core';

// Tipos locales para compatibilidad con TS 3.4
type CalendarViewMode = 'month' | 'week' | 'day';

interface ProductionCalendarEvent {
  id?: string | number;
  title: string;
  start: Date | string;
  end?: Date | string;
  color?: {
    primary?: string;
    secondary?: string;
  };
  meta?: any;
}

@Component({
  selector: 'app-schedule-list',
  templateUrl: './schedule-list.component.html'
})
export class ScheduleListComponent implements OnInit {
  newCalendarView: CalendarViewMode = 'month';
  newCalendarEvents: ProductionCalendarEvent[] = [];
  viewDate: Date = new Date();

  // ... resto del componente
}
```

### 4. Usar en el template

```html
<!-- schedule-list.component.html -->
<production-calendar
  [attr.viewDate]="viewDate.toISOString()"
  [attr.view]="newCalendarView"
  (dayClick)="onDayClick($event)"
  (eventClick)="onEventClick($event)">
</production-calendar>
```

### 5. Manejar eventos

```typescript
export class ScheduleListComponent {
  
  // Sincronizar eventos al Web Component
  syncEventsToNewCalendar() {
    this.newCalendarEvents = this.events.map((event: any) => ({
      id: event.id || 'evt-' + Math.random().toString(36).substr(2, 9),
      title: event.title,
      start: event.start,
      end: event.end || event.start,
      color: {
        primary: (event.color && event.color.primary) || '#1e90ff',
        secondary: (event.color && event.color.secondary) || '#D1E8FF'
      },
      meta: event // Guardar evento completo para recuperar después
    }));

    // Actualizar Web Component
    setTimeout(() => {
      const calendarElement: any = document.querySelector('production-calendar');
      if (calendarElement) {
        calendarElement.events = this.newCalendarEvents;
        calendarElement.viewDate = this.viewDate;
        calendarElement.view = this.newCalendarView;
      }
    }, 50);
  }

  // Manejar click en evento
  onEventClick(event: any) {
    const detail = event.detail;
    const originalEvent = detail.event.meta; // Recuperar evento original
    
    // ✨ Nuevo en v1.1.0: Usar la fecha clickeada en lugar de la fecha de inicio
    const clickedDate = detail.clickedDate 
      ? (typeof detail.clickedDate === 'string' ? new Date(detail.clickedDate) : detail.clickedDate)
      : (typeof detail.event.start === 'string' ? new Date(detail.event.start) : detail.event.start);
    
    // Abrir modal o realizar acción con la fecha exacta donde se hizo clic
    this.openDialog(originalEvent, clickedDate);
  }

  // Manejar click en día
  onDayClick(event: any) {
    const detail = event.detail;
    console.log('Día clickeado:', detail.date);
    console.log('Eventos del día:', detail.events);
  }
}
```

---

## 📖 API Completa

### Propiedades

| Propiedad | Tipo | Default | Descripción |
|-----------|------|---------|-------------|
| `events` | `ProductionCalendarEvent[]` | `[]` | Lista de eventos a mostrar |
| `viewDate` | `Date \| string` | `new Date()` | Fecha central del calendario |
| `view` | `'month' \| 'week' \| 'day'` | `'month'` | Vista actual del calendario |
| `showEventTime` | `boolean \| string` | `false` | Mostrar u ocultar la hora de los eventos |

### Eventos Emitidos

| Evento | Tipo del Detail | Descripción |
|--------|-----------------|-------------|
| `dayClick` | `{ date: Date, events: ProductionCalendarEvent[] }` | Se dispara al hacer clic en un día |
| `eventClick` | `{ event: ProductionCalendarEvent, clickedDate: Date, sourceEvent: Event }` | Se dispara al hacer clic en un evento |
| `viewChange` | `{ view: CalendarViewMode }` | Se dispara al cambiar la vista |

> **✨ Nuevo en v1.1.0**: El evento `eventClick` ahora incluye `clickedDate`, que representa la fecha de la celda del calendario donde se hizo clic. Esto es especialmente útil para eventos de múltiples días, donde `event.start` puede ser diferente de la fecha clickeada.

### Control de Visualización de Horas

Por defecto, las horas de los eventos **no se muestran** (`showEventTime = false`). Puedes controlar esto de dos formas:

**Opción 1: Como atributo HTML**
```html
<production-calendar
  [attr.showEventTime]="true"
  [attr.viewDate]="viewDate.toISOString()"
  [attr.view]="newCalendarView">
</production-calendar>
```

**Opción 2: Vía JavaScript/TypeScript**
```typescript
const calendarElement: any = document.querySelector('production-calendar');
calendarElement.showEventTime = true;  // Mostrar horas
calendarElement.showEventTime = false; // Ocultar horas (default)
```

**Resultado:**
- `showEventTime = false` (default): "OP-2026-001 Ensamble Motores"
- `showEventTime = true`: "12:00 a.m. OP-2026-001 Ensamble Motores"

> **💡 Tip**: Para eventos de producción, usualmente la hora exacta no es relevante, por eso está oculta por defecto. Actívala solo si tus eventos tienen horarios específicos importantes.

### Tipo ProductionCalendarEvent

```typescript
interface ProductionCalendarEvent {
  id?: string | number;           // ID único del evento
  title: string;                  // Título a mostrar
  start: Date | string;           // Fecha/hora inicio (ISO string o Date)
  end?: Date | string;            // Fecha/hora fin (opcional)
  color?: {
    primary?: string;             // Color principal (#hex)
    secondary?: string;           // Color secundario/fondo
  };
  meta?: Record<string, any>;    // Metadatos personalizados
  draggable?: boolean;            // Si se puede arrastrar (futuro)
  allDay?: boolean;               // Si es evento de todo el día
}
```

### 📅 Manejo de Eventos Multi-día

Cuando un evento tiene duración múltiple (ej: del 1 al 6 de marzo), aparece en varias celdas del calendario. **Desde la v1.1.0**, puedes obtener la fecha exacta donde el usuario hizo clic:

```typescript
onEventClick(event: any) {
  const detail = event.detail;
  
  // Fecha de inicio del evento (siempre es la misma)
  const eventStartDate = detail.event.start; // "2026-03-01"
  
  // Fecha donde el usuario hizo clic (nueva en v1.1.0)
  const clickedDate = detail.clickedDate;     // "2026-03-04" si clickeó el día 4
  
  // Ejemplo: Mostrar "Programación del 04/03/2026" en vez de "01/03/2026"
  this.displayModal({
    title: detail.event.title,
    displayDate: clickedDate,  // Usar fecha clickeada
    eventData: detail.event.meta
  });
}
```

**Beneficios:**
- ✅ Mejor UX: muestra la fecha que el usuario realmente seleccionó
- ✅ Útil para modales de detalle o edición
- ✅ Compatible hacia atrás: usa `event.start` si no necesitas esta funcionalidad

---

## 🎨 Personalización

### Usando CSS Variables

El calendario usa CSS Variables para fácil personalización:

```css
production-calendar {
  --calendar-bg: #ffffff;
  --calendar-header-bg: #f8fafc;
  --calendar-border: #e2e8f0;
  --text-primary: #1f2937;
  --text-secondary: #64748b;
  --accent: #3b82f6;
  --accent-light: #60a5fa;
}
```

### Z-index y Modales

El calendario usa `z-index: 100-101` para el panel lateral. Los modales de Angular Material (z-index ~1000) quedarán correctamente por encima.

---

## 🔧 Troubleshooting

### Error: "Declaration or statement expected"

**Causa**: Angular 8 usa TypeScript 3.4 que no soporta `import type`.

**Solución**: Define los tipos localmente en tu componente (ver sección de integración).

### El modal de Angular queda detrás del panel

**Causa**: Conflicto de z-index.

**Solución**: Ya está resuelto en v1.0.2+. El calendario usa z-index bajo (100-101).

### Eventos no se actualizan

**Solución**: Accede al elemento y actualiza la propiedad directamente:

```typescript
const cal: any = document.querySelector('production-calendar');
cal.events = newEvents;
cal.viewDate = newDate;
```

---

## � Compatibilidad y Build

### Soporte de ES2015 para Angular 8+

**Desde v1.2.0**, la librería se compila con target **ES2015** para garantizar compatibilidad con:
- ✅ Angular 8+ (TypeScript 3.4+)
- ✅ Webpack 4.x con UglifyJS/Terser antiguo
- ✅ Navegadores desde IE11+ (con polyfills)

### Problema Resuelto

**Antes de v1.2.0:**
```bash
ERROR in scripts.xxx.js from Terser
Unexpected token: punc (;) [scripts.xxx.js:3677,34]
```

**Causa**: La librería usaba ES2022, que incluye operadores modernos (`?.`, `??`) no soportados por las versiones de Terser incluidas en Angular 8.

**Uso recomendado:**
- Angular 8-11: Usar **UMD** en `angular.json` scripts
- Angular 12+: Puede usar **ESM** directamente

---

## �🛠️ Desarrollo Local

```bash
# Clonar e instalar
git clone <repo-url>
cd lib-wc-vite-lit-calendar
npm install

# Desarrollo
npm run dev

# Build para producción
npm run build

# Publicar
npm version patch
npm publish
```

---

## 📜 Changelog

### v1.2.1 (2026-04-09)
- ✨ **Nueva propiedad `showEventTime`**: Control para mostrar/ocultar horas de eventos
- 🎯 **Default mejorado**: Las horas ahora están ocultas por defecto (`showEventTime = false`)
- 💡 **UX optimizada**: Enfoque en eventos de producción donde la hora suele no ser relevante
- 📝 **Documentación actualizada**: Guía completa de uso de `showEventTime`
- ⚙️ Soporte para configuración vía atributo HTML o propiedad JavaScript

### v1.2.0 (2026-04-09)
- 🔧 **Compatibilidad mejorada con Angular 8+**: Transpilación a ES2015 para evitar errores de Terser
- ✅ Resuelto error `Unexpected token` en builds de producción (`--prod`)
- 📦 Agregado `@types/node` para soporte completo de TypeScript
- ⚙️ Configuración de build optimizada (Vite + Terser ES2015)
- 🎯 Target ES2015 en tsconfig y vite.config para máxima compatibilidad
- 🛠️ Elimina errores de minificación en Angular 8 con webpack/uglify antiguos

**Migración desde v1.1.x**: No requiere cambios en tu código, solo actualiza:
```bash
npm update @nuvaprd/production-calendar
```

### v1.1.0 (2026-04-09)
- ✨ **Nueva funcionalidad**: `eventClick` ahora incluye `clickedDate` en el detail
- 🎯 Mejora para eventos multi-día: distingue entre fecha de inicio del evento y fecha donde se hizo clic
- 📝 Útil para modales que necesitan mostrar la fecha específica seleccionada por el usuario
- 🔄 Compatibilidad hacia atrás mantenida: el código existente sigue funcionando

### v1.0.4 (2026-03-28)
- ✅ Ajuste en el contraste de las letras

### v1.0.2 (2026-03-28)
- ✅ Ajustado z-index para compatibilidad con modales de Angular
- ✅ Cambiada fuente a Roboto para consistencia con aplicaciones Angular
- ✅ Mejorada sincronización de eventos con meta

### v1.0.1 (2026-03-28)
- ✅ Corregida exportación de tipos para TypeScript 3.4
- ✅ Mejorada compatibilidad con Angular 8+

### v1.0.0 (2026-03-27)
- 🎉 Lanzamiento inicial
- ✅ Vistas Mes, Semana, Día
- ✅ Panel lateral con Lista y Gantt
- ✅ API compatible con angular-calendar

---

## 📄 Licencia

MIT © [Nuvaprd]

---

**Hecho con ❤️ usando [Lit](https://lit.dev) y [Vite](https://vitejs.dev)**


```bash
# En el proyecto Angular de producción:
npm update @nuva/production-calendar

# O para forzar la última versión:
npm install @nuva/production-calendar@latest
```

--

## 🎯 Migración desde angular-calendar

| angular-calendar | production-calendar |
|------------------|---------------------|
| `mwl-calendar-month-view` | `production-calendar` |
| `[viewDate]="viewDate"` | `calendar.viewDate = viewDate` |
| `[events]="events"` | `calendar.events = events` |
| `(dayClicked)="fn($event)"` | `(dayClick)="fn($event)"` |
| `(eventClicked)="fn($event)"` | `(eventClick)="fn($event)"` |
| `CalendarEvent` | `ProductionCalendarEvent` |

---

## 📋 Checklist de release

- [ ] Código probado con `npm run dev`
- [ ] Build sin errores: `npm run build`
- [ ] Versión actualizada: `npm version <tipo>`
- [ ] Publicado: `npm publish`
- [ ] Tag en git: `git push --tags`