diff --git a/src/app/components/pages/planning/planning.css b/src/app/components/pages/planning/planning.css
index 229c75f5..4652dcf9 100644
--- a/src/app/components/pages/planning/planning.css
+++ b/src/app/components/pages/planning/planning.css
@@ -317,8 +317,6 @@
/* ── Événements dans la grille ──────────────────────────── */
.show-event {
position: absolute;
- left: 3px;
- right: 3px;
height: 42px;
background: linear-gradient(135deg, #d4a574, #c49563);
color: #fff;
@@ -348,8 +346,6 @@
.truck-event {
position: absolute;
- left: 3px;
- right: 3px;
height: 42px;
background: linear-gradient(135deg, #605DC8, #4e4aaa);
color: #fff;
@@ -873,3 +869,58 @@
opacity: 0.45;
cursor: not-allowed;
}
+
+/* ── Hint dans le formulaire ────────────────────────────── */
+.form-hint {
+ font-size: 10px;
+ color: #bbb;
+ margin-top: 5px;
+ font-style: italic;
+ line-height: 1.4;
+}
+
+/* ── Badge statut camion dans truck-tag ─────────────────── */
+.truck-tag-statut {
+ font-size: 9px;
+ font-weight: 700;
+ padding: 1px 5px;
+ border-radius: 8px;
+ background: rgba(96, 93, 200, 0.12);
+ color: #605DC8;
+}
+
+/* ── Couleurs selon statut ──────────────────────────────── */
+.statut-disponible {
+ color: #3aaa6e !important;
+}
+
+.statut-indisponible {
+ color: #e05252 !important;
+}
+
+.statut-en-déplacement {
+ color: #d4a574 !important;
+}
+
+.statut-en-panne {
+ color: #e05252 !important;
+}
+
+.statut-en-maintenance {
+ color: #888 !important;
+}
+
+.show-delete-btn {
+ border: none;
+ background: none;
+ color: #ccc;
+ cursor: pointer;
+ font-size: 16px;
+ line-height: 1;
+ padding: 0 2px;
+ transition: color 0.15s;
+}
+
+.show-delete-btn:hover {
+ color: #e05252;
+}
diff --git a/src/app/components/pages/planning/planning.html b/src/app/components/pages/planning/planning.html
index 3c7efdf5..2885d69b 100644
--- a/src/app/components/pages/planning/planning.html
+++ b/src/app/components/pages/planning/planning.html
@@ -62,7 +62,6 @@
{{ date.getDate() }}
-
@if (isToday(date)) {
}
@@ -71,16 +70,20 @@
(click)="selectDay(date)">
@for (show of getShowsForSlot(date, hour); track show.id) {
+ [style.top.px]="show.date ? getEventTopPx(show.date) : 0"
+ [style.left]="'calc(' + (show.colIndex / show.colCount * 100) + '% + 3px)'"
+ [style.right]="'calc(' + ((show.colCount - show.colIndex - 1) / show.colCount * 100) + '% + 3px)'">
{{ show.name }}
{{ formatShowTime(show.date) }}
}
@for (truck of getTrucksForSlot(date, hour); track truck.id) {
+ [style.top.px]="truck.showId ? 0 : 0"
+ [style.left]="'calc(' + (truck.colIndex / truck.colCount * 100) + '% + 3px)'"
+ [style.right]="'calc(' + ((truck.colCount - truck.colIndex - 1) / truck.colCount * 100) + '% + 3px)'">
{{ truck.type || 'Camion' }}
- {{ truck.statut }}
+ {{ getComputedTruckStatus(truck) }}
}
@@ -153,7 +156,10 @@
{{ show.place }}
{{ show.description }}
@@ -161,6 +167,9 @@
@for (truck of getTrucksForShow(show.id!); track truck.id) {
{{ truck.type }}
+
+ {{ getComputedTruckStatus(truck) }}
+
@@ -200,7 +209,9 @@
@@ -212,7 +223,6 @@
}
-
@@ -331,16 +341,15 @@
+
@@ -355,4 +364,4 @@
-
\ No newline at end of file
+
diff --git a/src/app/components/pages/planning/planning.ts b/src/app/components/pages/planning/planning.ts
index 364578de..e322d281 100644
--- a/src/app/components/pages/planning/planning.ts
+++ b/src/app/components/pages/planning/planning.ts
@@ -85,7 +85,7 @@ export class Planning implements OnInit, AfterViewInit {
if (!this.newShow.name) return;
const dto: PyroFetesDTOShowRequestCreateShowDto = {
...this.newShow,
- date: this.newShowDate ? this.newShowDate.toISOString() : undefined,
+ date: this.newShowDate ? this.formatDateLocal(this.newShowDate) : undefined,
};
await firstValueFrom(this.showsServices.pyroFetesEndpointsShowCreateShowEndpoint(dto));
await this.fetchShows();
@@ -94,6 +94,16 @@ export class Planning implements OnInit, AfterViewInit {
this.isVisible.set(false);
}
+ private formatDateLocal(date: Date): string {
+ const pad = (n: number) => n.toString().padStart(2, '0');
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:00`;
+ }
+
+ async deleteShow(showId: number): Promise {
+ await firstValueFrom(this.showsServices.pyroFetesEndpointsShowDeleteShowEndpoint(showId));
+ await this.fetchShows();
+ }
+
handleCancel(): void {
this.newShow = {};
this.newShowDate = null;
@@ -207,15 +217,43 @@ export class Planning implements OnInit, AfterViewInit {
this.selectedDay = date;
}
- // Both filters off → show everything. One active → only that type. Both active → both types.
+ // ── Filtres ──────────────────────────────────────────────────
+ // Aucun filtre actif → rien ne s'affiche. Chaque filtre affiche uniquement son type.
get shouldShowShows(): boolean {
- return !this.isCamionFilterActive || this.isShowFilterActive;
+ return this.isShowFilterActive;
}
get shouldShowCamions(): boolean {
- return !this.isShowFilterActive || this.isCamionFilterActive;
+ return this.isCamionFilterActive;
}
+ // ── Statut automatique des camions ───────────────────────────
+ // Priorité : En panne / En maintenance (manuel) → toujours respectés
+ // Sinon calculé selon le show associé et l'heure actuelle
+ getComputedTruckStatus(truck: PyroFetesDTOTruckResponseReadTruckDto): string {
+ // Statuts manuels prioritaires, jamais écrasés
+ if (truck.statut === 'En panne' || truck.statut === 'En maintenance') {
+ return truck.statut;
+ }
+
+ // Pas de show assigné → disponible
+ if (!truck.showId) return 'Disponible';
+
+ // Cherche le show associé
+ const show = this.shows().find(s => s.id === truck.showId);
+ if (!show?.date) return 'Disponible';
+
+ const showDate = new Date(show.date);
+ const now = new Date();
+ const diffMs = showDate.getTime() - now.getTime();
+ const diffH = diffMs / (1000 * 60 * 60);
+
+ if (diffH < 0) return 'Disponible'; // show passé → camion libre
+ if (diffH < 1) return 'En déplacement'; // moins d'1h avant le show
+ return 'Indisponible'; // show futur (>= 1h)
+ }
+
+ // ── Slots et grille ──────────────────────────────────────────
getShowsForDay(date: Date): PyroFetesDTOShowResponseReadShowDto[] {
if (!this.shouldShowShows) return [];
return this.shows().filter(show => {
@@ -227,9 +265,33 @@ export class Planning implements OnInit, AfterViewInit {
});
}
- getShowsForSlot(date: Date, hour: string): PyroFetesDTOShowResponseReadShowDto[] {
+ getShowsForSlot(date: Date, hour: string): (PyroFetesDTOShowResponseReadShowDto & { colIndex: number; colCount: number })[] {
if (!this.shouldShowShows) return [];
const h = hour === '' ? 0 : parseInt(hour);
+ const shows = this.shows().filter(show => {
+ if (!show.date) return false;
+ const d = new Date(show.date);
+ return d.getFullYear() === date.getFullYear() &&
+ d.getMonth() === date.getMonth() &&
+ d.getDate() === date.getDate() &&
+ d.getHours() === h;
+ });
+ const trucksPresent = this.shouldShowCamions && this.getTrucksRawForSlot(date, h).length > 0;
+ const colCount = trucksPresent ? 2 : 1;
+ return shows.map(s => ({ ...s, colIndex: 0, colCount }));
+ }
+
+ getTrucksForSlot(date: Date, hour: string): (PyroFetesDTOTruckResponseReadTruckDto & { colIndex: number; colCount: number })[] {
+ if (!this.shouldShowCamions) return [];
+ const h = hour === '' ? 0 : parseInt(hour);
+ const trucks = this.getTrucksRawForSlot(date, h);
+ const showsPresent = this.shouldShowShows && this.getShowsRawForSlot(date, h).length > 0;
+ const colCount = showsPresent ? 2 : 1;
+ return trucks.map(t => ({ ...t, colIndex: showsPresent ? 1 : 0, colCount }));
+ }
+
+ // helpers internes
+ private getShowsRawForSlot(date: Date, h: number): PyroFetesDTOShowResponseReadShowDto[] {
return this.shows().filter(show => {
if (!show.date) return false;
const d = new Date(show.date);
@@ -240,9 +302,7 @@ export class Planning implements OnInit, AfterViewInit {
});
}
- getTrucksForSlot(date: Date, hour: string): PyroFetesDTOTruckResponseReadTruckDto[] {
- if (!this.shouldShowCamions) return [];
- const h = hour === '' ? 0 : parseInt(hour);
+ private getTrucksRawForSlot(date: Date, h: number): PyroFetesDTOTruckResponseReadTruckDto[] {
const showsInSlot = this.shows().filter(show => {
if (!show.date) return false;
const d = new Date(show.date);
@@ -282,10 +342,12 @@ export class Planning implements OnInit, AfterViewInit {
return this.trucks().filter(t => t.showId === showId);
}
+ // Retourne uniquement les camions dont le statut calculé est "Disponible"
getAvailableTrucks(): PyroFetesDTOTruckResponseReadTruckDto[] {
- return this.trucks().filter(t => !t.showId);
+ return this.trucks().filter(t => this.getComputedTruckStatus(t) === 'Disponible');
}
+ // ── Actions camions ──────────────────────────────────────────
async assignTruck(truckId: number, showId: number): Promise {
await firstValueFrom(this.trucksService.pyroFetesEndpointsTruckUpdateTruckEndpoint(
truckId, { showId }
@@ -371,8 +433,8 @@ export class Planning implements OnInit, AfterViewInit {
return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
}
- isCamionFilterActive = false;
- isShowFilterActive = false;
+ isCamionFilterActive = true;
+ isShowFilterActive = true;
camionFilter(): void {
this.isCamionFilterActive = !this.isCamionFilterActive;