V5.0.0 //

-> Refonte du système de GESTION DES ROULEMENTS de matériels roulants
-> Optimisation rendue BDD
This commit is contained in:
AdminGit committed 2026-10-03 17:33:53 +02:00
1 parent 36b0c00cbc
commit f77d59ca1c
25 files changed
+16567 -6198

No files matched your search

+393 -274
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import NavPanel from '../../components/nav-panel/NavPanel';
import { createTravaux, getHorairesSubstitution } from '../../api/travaux';
import { getAllLignes } from '../../api/lignes';
@@ -21,6 +21,9 @@ export default function CreerTravaux() {
const currentUser = getCurrentUser();
const canCreateTravaux = canPerform(currentUser, 'travaux', 'create');
const [activeTabKey, setActiveTabKey] = useState('general');
const tabsRef = useRef(null);
const [titre, setTitre] = useState('');
const [description, setDescription] = useState('');
const [dateDebut, setDateDebut] = useState('');
@@ -62,6 +65,21 @@ export default function CreerTravaux() {
fetchLignes();
}, [scopedRegionId]);
// Synchronisation des wcs-tabs
useEffect(() => {
const el = tabsRef.current;
if (!el) return;
const handleTabChange = (e) => {
if (e.detail?.selectedKey) {
setActiveTabKey(e.detail.selectedKey);
}
};
el.addEventListener('wcsChange', handleTabChange);
return () => {
el.removeEventListener('wcsChange', handleTabChange);
};
}, []);
const handleSearchHoraires = useCallback(async () => {
setLoadingHoraires(true);
setShowResults(true);
@@ -108,7 +126,7 @@ export default function CreerTravaux() {
setJoursTravaux(['samedi', 'dimanche']);
};
const formatHeure = (heure) => heure ? heure.substring(0, 5) : '—';
const formatHeure = (heure) => (heure ? heure.substring(0, 5) : '—');
const navigate = (path) => { window.location.href = path; };
@@ -163,20 +181,33 @@ export default function CreerTravaux() {
<div className="panel-nav">
<NavPanel />
</div>
<div className="panel-content travaux-form-page-content">
{/* Header Banner */}
{/* Header Banner SNCF avec composants WCS */}
<header className="travaux-form-header-modern">
<div className="form-header-main">
<button type="button" className="btn-back-link" onClick={() => navigate('/travaux')}>
<wcs-mat-icon icon="arrow_back"></wcs-mat-icon>
<wcs-button
mode="stroked"
size="s"
className="wcs-secondary mb-2"
onClick={() => navigate('/travaux')}
>
<wcs-mat-icon icon="arrow_back" size="s" class="mr-1"></wcs-mat-icon>
<span>Retour aux travaux</span>
</button>
</wcs-button>
<div className="form-title-group">
<span className="travaux-kicker-pill">
<wcs-mat-icon icon="add_circle"></wcs-mat-icon>
Nouveau Chantier
</span>
<div className="d-flex align-items-center gap-2 mb-1">
<wcs-badge shape="pill" className="wcs-info">
<wcs-mat-icon icon="add_circle" size="s" class="mr-1"></wcs-mat-icon>
Nouveau Chantier
</wcs-badge>
{scopedRegionId && (
<wcs-badge shape="pill" className="wcs-secondary">
Région : {scopedRegionId}
</wcs-badge>
)}
</div>
<h1>Déclarer des travaux sur le réseau</h1>
<p>Planifiez une coupure, des restrictions de circulation et les substitutions associées.</p>
</div>
@@ -184,309 +215,397 @@ export default function CreerTravaux() {
<div className="form-header-actions">
<wcs-button className="wcs-secondary" mode="stroked" onClick={() => navigate('/travaux')}>
Annuler
<span>Annuler</span>
</wcs-button>
<wcs-button
className="wcs-primary"
<wcs-button
className="wcs-primary"
onClick={handleCreate}
disabled={saving || !canCreateTravaux}
loading={saving}
>
{saving ? (
<>
<wcs-spinner mode="border" style={{ width: '16px', height: '16px', marginRight: '6px' }}></wcs-spinner>
<span>Enregistrement...</span>
</>
) : (
<>
<wcs-mat-icon icon="save" style={{ marginRight: '6px' }}></wcs-mat-icon>
<span>Créer le chantier</span>
</>
)}
<wcs-mat-icon icon="save" size="s" class="mr-1"></wcs-mat-icon>
<span>Créer le chantier</span>
</wcs-button>
</div>
</header>
{/* Navigation par Onglets WCS */}
<div className="mb-4">
<wcs-tabs ref={tabsRef} selected-key={activeTabKey}>
<wcs-tab header="1. Général & Ligne" item-key="general"></wcs-tab>
<wcs-tab header="2. Calendrier & Période" item-key="calendrier"></wcs-tab>
<wcs-tab header={`3. Substitutions (${horairesSelectionnes.length})`} item-key="substitutions"></wcs-tab>
</wcs-tabs>
</div>
<div className="travaux-form-grid">
{/* Section 1 : Informations générales */}
<div className="travaux-glass-card">
<div className="section-title-wrap">
<div className="section-icon-box">
<wcs-mat-icon icon="info"></wcs-mat-icon>
</div>
<div>
<h2 className="travaux-section-title">Informations Générales</h2>
<p className="travaux-section-subtitle">Définissez l'intitulé, la ligne concernée et les consignes d'exploitation.</p>
</div>
</div>
<div className="premium-input-group">
<label className="premium-label">Titre des travaux *</label>
<input
type="text"
className="premium-input"
placeholder="Ex: Rénovation caténaire et voies Paris vers Lyon"
value={titre}
onChange={(e) => setTitre(e.target.value)}
/>
</div>
<div className="premium-input-group">
<label className="premium-label">Ligne ferroviaire associée</label>
<select
className="premium-input"
value={selectedLigneId}
onChange={(e) => setSelectedLigneId(e.target.value)}
disabled={loadingLignes}
>
<option value="">Sélectionner une ligne (optionnel)</option>
{lignes.map(l => (
<option key={l.id} value={l.id}>{l.nom || l.numero || `Ligne ${l.id}`}</option>
))}
</select>
</div>
<div className="premium-input-group">
<label className="premium-label">Description détaillée & Informations voyageurs</label>
<RichTextEditor
value={description}
onChange={setDescription}
placeholder="Détaillez les impacts prévus, motifs techniques, gares concernées..."
/>
</div>
</div>
{/* Section 2 : Planification & Calendrier */}
<div className="travaux-glass-card">
<div className="section-title-wrap">
<div className="section-icon-box">
<wcs-mat-icon icon="calendar_month"></wcs-mat-icon>
</div>
<div>
<h2 className="travaux-section-title">Planification & Horaires</h2>
<p className="travaux-section-subtitle">Période de validité du chantier, jours impactés et créneaux journaliers.</p>
</div>
</div>
<div className="form-columns-2">
<div className="premium-input-group">
<label className="premium-label">Date de début *</label>
<input type="date" className="premium-input" value={dateDebut} onChange={(e) => setDateDebut(e.target.value)} />
</div>
<div className="premium-input-group">
<label className="premium-label">Date de fin *</label>
<input type="date" className="premium-input" value={dateFin} onChange={(e) => setDateFin(e.target.value)} />
</div>
</div>
<div className="form-columns-2">
<div className="premium-input-group">
<label className="premium-label">Heure de début journalière *</label>
<input type="time" className="premium-input" value={heureDebut} onChange={(e) => setHeureDebut(e.target.value)} />
</div>
<div className="premium-input-group">
<label className="premium-label">Heure de fin journalière *</label>
<input type="time" className="premium-input" value={heureFin} onChange={(e) => setHeureFin(e.target.value)} />
</div>
</div>
<div className="premium-input-group" style={{ marginTop: '12px' }}>
<div className="days-header-row">
<label className="premium-label">Jours impactés dans la semaine *</label>
<div className="days-shortcuts">
<button type="button" className="btn-shortcut" onClick={selectAllDays}>Tous les jours</button>
<button type="button" className="btn-shortcut" onClick={selectWeekdays}>Semaine</button>
<button type="button" className="btn-shortcut" onClick={selectWeekend}>Week-end</button>
</div>
</div>
<div className="premium-checkbox-group">
{JOURS_SEMAINE.map((jour) => (
<label key={jour.value} className={`premium-checkbox-label ${joursTravaux.includes(jour.value) ? 'selected' : ''}`}>
<input type="checkbox" checked={joursTravaux.includes(jour.value)} onChange={() => toggleJour(jour.value)} />
<span>{jour.label}</span>
</label>
))}
</div>
</div>
</div>
{/* Section 3 : Substitutions */}
<div className="travaux-glass-card">
<div className="section-title-wrap">
<div className="section-icon-box">
<wcs-mat-icon icon="swap_horiz"></wcs-mat-icon>
</div>
<div>
<h2 className="travaux-section-title">Plan de Transport de Substitution</h2>
<p className="travaux-section-subtitle">Associez des cars ou trains spéciaux pour remplacer les circulations supprimées.</p>
</div>
</div>
<label className={`premium-toggle-switch ${substitutionsActive ? 'active' : ''}`}>
<input
type="checkbox"
checked={substitutionsActive}
onChange={(e) => setSubstitutionsActive(e.target.checked)}
/>
<span className="switch-slider"></span>
<span className="switch-text">
<strong>Activer le plan de substitution pour ce chantier</strong>
<small>Permet de relier des horaires de cars ou trains de substitution actifs</small>
</span>
</label>
{substitutionsActive && (
<div className="substitutions-module-body">
<div className="substitutions-search-bar">
<div className="search-input-wrap">
<wcs-mat-icon icon="search" className="search-icon"></wcs-mat-icon>
<input
type="text"
className="premium-input"
placeholder="Rechercher par numéro de train, gare de départ ou terminus..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') handleSearchHoraires(); }}
/>
{/* Onglet 1 : Informations Générales */}
{(activeTabKey === 'general' || activeTabKey === 'all') && (
<wcs-card className="wcs-card-form mb-4">
<wcs-card-body>
<div className="section-title-wrap mb-3">
<div className="section-icon-box">
<wcs-mat-icon icon="info" size="m"></wcs-mat-icon>
</div>
<wcs-button
className="wcs-primary"
onClick={handleSearchHoraires}
disabled={loadingHoraires}
<div>
<h2 className="travaux-section-title">Informations Générales</h2>
<p className="travaux-section-subtitle">Définissez l'intitulé, la ligne concernée et les consignes d'exploitation.</p>
</div>
</div>
<wcs-form-field class="mb-3">
<label htmlFor="titre-travaux">Titre des travaux *</label>
<input
id="titre-travaux"
type="text"
className="form-control"
placeholder="Ex: Rénovation caténaire et voies Paris vers Lyon"
value={titre}
onChange={(e) => setTitre(e.target.value)}
/>
</wcs-form-field>
<wcs-form-field class="mb-3">
<label htmlFor="ligne-select">Ligne ferroviaire associée</label>
<select
id="ligne-select"
className="form-control"
value={selectedLigneId}
onChange={(e) => setSelectedLigneId(e.target.value)}
disabled={loadingLignes}
>
<wcs-mat-icon icon="search" style={{ marginRight: '6px' }}></wcs-mat-icon>
<span>Rechercher</span>
</wcs-button>
<option value="">Sélectionner une ligne (optionnel)</option>
{lignes.map(l => (
<option key={l.id} value={l.id}>{l.nom || l.numero || `Ligne ${l.id}`}</option>
))}
</select>
</wcs-form-field>
<div className="mb-3">
<label className="form-label font-weight-bold d-block mb-2">Description détaillée & Informations voyageurs</label>
<RichTextEditor
value={description}
onChange={setDescription}
placeholder="Détaillez les impacts prévus, motifs techniques, gares concernées..."
/>
</div>
</wcs-card-body>
</wcs-card>
)}
{/* Onglet 2 : Planification & Calendrier */}
{(activeTabKey === 'calendrier' || activeTabKey === 'all') && (
<wcs-card className="wcs-card-form mb-4">
<wcs-card-body>
<div className="section-title-wrap mb-3">
<div className="section-icon-box">
<wcs-mat-icon icon="calendar_month" size="m"></wcs-mat-icon>
</div>
<div>
<h2 className="travaux-section-title">Planification & Horaires</h2>
<p className="travaux-section-subtitle">Période de validité du chantier, jours impactés et créneaux journaliers.</p>
</div>
</div>
{/* Horaires sélectionnés */}
{horairesSelectionnes.length > 0 && (
<div className="selected-subs-container">
<div className="selected-subs-header">
<h4>
<wcs-mat-icon icon="check_circle" style={{ color: '#0088ce', marginRight: '6px' }}></wcs-mat-icon>
{horairesSelectionnes.length} circulation{horairesSelectionnes.length > 1 ? 's' : ''} de substitution sélectionnée{horairesSelectionnes.length > 1 ? 's' : ''}
</h4>
</div>
<div className="selected-subs-list">
{horairesSelectionnes.map(h => (
<div key={h.id} className="selected-sub-card">
<div className="sub-main-col">
<div className="sub-num-badge">
<wcs-mat-icon icon="directions_bus"></wcs-mat-icon>
<strong>{h.numero_train || 'Train'}</strong>
{h.type_train && <wcs-badge className="wcs-info" shape="round">{h.type_train}</wcs-badge>}
</div>
<div className="sub-route">
<span>{h.gare_depart_nom} ({formatHeure(h.heure_depart)})</span>
<wcs-mat-icon icon="arrow_forward" className="arrow-icon"></wcs-mat-icon>
<span>{h.gare_arrivee_nom} ({formatHeure(h.heure_arrivee)})</span>
</div>
</div>
<button
type="button"
className="btn-remove-sub"
onClick={() => toggleHoraireSelection(h)}
title="Retirer cette substitution"
>
<wcs-mat-icon icon="close"></wcs-mat-icon>
</button>
</div>
))}
<div className="form-columns-2 mb-3">
<wcs-form-field>
<label htmlFor="date-debut">Date de début *</label>
<input
id="date-debut"
type="date"
className="form-control"
value={dateDebut}
onChange={(e) => setDateDebut(e.target.value)}
/>
</wcs-form-field>
<wcs-form-field>
<label htmlFor="date-fin">Date de fin *</label>
<input
id="date-fin"
type="date"
className="form-control"
value={dateFin}
onChange={(e) => setDateFin(e.target.value)}
/>
</wcs-form-field>
</div>
<div className="form-columns-2 mb-3">
<wcs-form-field>
<label htmlFor="heure-debut">Heure de début journalière *</label>
<input
id="heure-debut"
type="time"
className="form-control"
value={heureDebut}
onChange={(e) => setHeureDebut(e.target.value)}
/>
</wcs-form-field>
<wcs-form-field>
<label htmlFor="heure-fin">Heure de fin journalière *</label>
<input
id="heure-fin"
type="time"
className="form-control"
value={heureFin}
onChange={(e) => setHeureFin(e.target.value)}
/>
</wcs-form-field>
</div>
<div className="mt-4">
<div className="days-header-row d-flex justify-content-between align-items-center mb-2">
<label className="font-weight-bold mb-0">Jours impactés dans la semaine *</label>
<div className="d-flex gap-2">
<wcs-button mode="stroked" size="s" onClick={selectAllDays}>Tous les jours</wcs-button>
<wcs-button mode="stroked" size="s" onClick={selectWeekdays}>Semaine</wcs-button>
<wcs-button mode="stroked" size="s" onClick={selectWeekend}>Week-end</wcs-button>
</div>
</div>
)}
{/* Résultats de recherche */}
{showResults && (
<div className="search-results-section">
<div className="search-results-header">
<h4>Résultats disponibles ({horairesDisponibles.length})</h4>
<span className="results-hint">Cliquez sur une circulation pour l'ajouter ou la retirer</span>
<div className="d-flex flex-wrap gap-2 mt-2">
{JOURS_SEMAINE.map((jour) => {
const isSelected = joursTravaux.includes(jour.value);
return (
<button
key={jour.value}
type="button"
className={`btn-day-pill ${isSelected ? 'is-selected' : ''}`}
onClick={() => toggleJour(jour.value)}
>
<wcs-mat-icon icon={isSelected ? 'check_circle' : 'circle_outline'} size="s" class="mr-1"></wcs-mat-icon>
<span>{jour.label}</span>
</button>
);
})}
</div>
</div>
</wcs-card-body>
</wcs-card>
)}
{/* Onglet 3 : Plan de Substitution */}
{(activeTabKey === 'substitutions' || activeTabKey === 'all') && (
<wcs-card className="wcs-card-form mb-4">
<wcs-card-body>
<div className="section-title-wrap mb-3">
<div className="section-icon-box">
<wcs-mat-icon icon="swap_horiz" size="m"></wcs-mat-icon>
</div>
<div>
<h2 className="travaux-section-title">Plan de Transport de Substitution</h2>
<p className="travaux-section-subtitle">Associez des cars ou trains spéciaux pour remplacer les circulations supprimées.</p>
</div>
</div>
{/* Interrupteur WCS Switch pour substitutions */}
<div className="d-flex align-items-center justify-content-between p-3 mb-4 rounded border bg-light">
<div>
<strong className="d-block text-dark">Activer le plan de substitution pour ce chantier</strong>
<small className="text-muted">Permet de relier des horaires de cars ou trains de substitution actifs</small>
</div>
<label className="switch-container">
<input
type="checkbox"
checked={substitutionsActive}
onChange={(e) => setSubstitutionsActive(e.target.checked)}
/>
<span className="switch-slider"></span>
</label>
</div>
{substitutionsActive && (
<div className="substitutions-module-body">
<div className="substitutions-search-bar d-flex gap-2 mb-3">
<div className="flex-grow-1 position-relative">
<input
type="text"
className="form-control"
placeholder="Rechercher par numéro de train, gare de départ ou terminus..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') handleSearchHoraires(); }}
/>
</div>
<wcs-button
className="wcs-primary"
onClick={handleSearchHoraires}
disabled={loadingHoraires}
loading={loadingHoraires}
>
<wcs-mat-icon icon="search" size="s" class="mr-1"></wcs-mat-icon>
<span>Rechercher</span>
</wcs-button>
</div>
{loadingHoraires ? (
<div className="search-loading-box">
<wcs-spinner mode="border"></wcs-spinner>
<p>Recherche des circulations en cours...</p>
</div>
) : horairesDisponibles.length === 0 ? (
<div className="search-empty-box">
<p>Aucun horaire de substitution trouvé pour « {searchQuery} ».</p>
</div>
) : (
<div className="subs-results-list">
{horairesDisponibles.map(h => {
const isSelected = horairesSelectionnes.some(hs => hs.id === h.id);
return (
<div
key={h.id}
className={`sub-result-card ${isSelected ? 'is-selected' : ''}`}
onClick={() => toggleHoraireSelection(h)}
>
<div className="result-info">
<div className="result-badge-row">
<strong>{h.numero_train}</strong>
{h.type_train && <wcs-badge className="wcs-info" shape="round">{h.type_train}</wcs-badge>}
{h.ligne_nom && <wcs-badge className="wcs-secondary" shape="round">{h.ligne_nom}</wcs-badge>}
{/* Horaires sélectionnés */}
{horairesSelectionnes.length > 0 && (
<div className="selected-subs-container mb-4">
<div className="selected-subs-header mb-2 d-flex align-items-center justify-content-between">
<h4 className="m-0 text-primary">
<wcs-mat-icon icon="check_circle" size="s" class="text-success mr-1"></wcs-mat-icon>
{horairesSelectionnes.length} circulation{horairesSelectionnes.length > 1 ? 's' : ''} de substitution sélectionnée{horairesSelectionnes.length > 1 ? 's' : ''}
</h4>
</div>
<div className="selected-subs-list d-flex flex-column gap-2">
{horairesSelectionnes.map(h => (
<div key={h.id} className="selected-sub-card d-flex align-items-center justify-content-between p-2 rounded border bg-white">
<div className="d-flex align-items-center gap-3">
<div className="d-flex align-items-center gap-2">
<wcs-mat-icon icon="directions_bus" size="s"></wcs-mat-icon>
<strong>{h.numero_train || 'Train'}</strong>
{h.type_train && <wcs-badge className="wcs-info" shape="pill">{h.type_train}</wcs-badge>}
</div>
<div className="result-route-row">
<div className="sub-route d-flex align-items-center gap-1 text-muted">
<span>{h.gare_depart_nom} ({formatHeure(h.heure_depart)})</span>
<wcs-mat-icon icon="arrow_forward" className="arrow-icon"></wcs-mat-icon>
<wcs-mat-icon icon="arrow_forward" size="s"></wcs-mat-icon>
<span>{h.gare_arrivee_nom} ({formatHeure(h.heure_arrivee)})</span>
</div>
</div>
<div className="result-checkbox">
<wcs-mat-icon icon={isSelected ? "check_box" : "check_box_outline_blank"} className={isSelected ? "icon-checked" : ""}></wcs-mat-icon>
</div>
<wcs-button
mode="clear"
className="wcs-danger"
size="s"
onClick={() => toggleHoraireSelection(h)}
title="Retirer cette substitution"
>
<wcs-mat-icon icon="close" size="s"></wcs-mat-icon>
</wcs-button>
</div>
);
})}
))}
</div>
</div>
)}
{/* Résultats de recherche */}
{showResults && (
<div className="search-results-section">
<div className="search-results-header mb-2 d-flex align-items-center justify-content-between">
<h4 className="m-0">Résultats disponibles ({horairesDisponibles.length})</h4>
<span className="text-muted small">Cliquez sur une circulation pour l'ajouter ou la retirer</span>
</div>
{loadingHoraires ? (
<div className="text-center p-4">
<wcs-spinner mode="border"></wcs-spinner>
<p className="mt-2 text-muted">Recherche des circulations en cours...</p>
</div>
) : horairesDisponibles.length === 0 ? (
<div className="text-center p-4 bg-light rounded text-muted">
<p className="m-0">Aucun horaire de substitution trouvé pour « {searchQuery} ».</p>
</div>
) : (
<div className="subs-results-list d-flex flex-column gap-2">
{horairesDisponibles.map(h => {
const isSelected = horairesSelectionnes.some(hs => hs.id === h.id);
return (
<div
key={h.id}
className={`sub-result-card d-flex align-items-center justify-content-between p-2 rounded border cursor-pointer ${isSelected ? 'border-primary bg-primary-soft' : 'bg-white'}`}
onClick={() => toggleHoraireSelection(h)}
style={{ cursor: 'pointer' }}
>
<div className="result-info">
<div className="d-flex align-items-center gap-2 mb-1">
<strong>{h.numero_train}</strong>
{h.type_train && <wcs-badge className="wcs-info" shape="pill">{h.type_train}</wcs-badge>}
{h.ligne_nom && <wcs-badge className="wcs-secondary" shape="pill">{h.ligne_nom}</wcs-badge>}
</div>
<div className="d-flex align-items-center gap-1 text-muted small">
<span>{h.gare_depart_nom} ({formatHeure(h.heure_depart)})</span>
<wcs-mat-icon icon="arrow_forward" size="s"></wcs-mat-icon>
<span>{h.gare_arrivee_nom} ({formatHeure(h.heure_arrivee)})</span>
</div>
</div>
<div>
<wcs-badge className={isSelected ? 'wcs-success' : 'wcs-secondary'} shape="pill">
<wcs-mat-icon icon={isSelected ? 'check' : 'add'} size="s"></wcs-mat-icon>
<span>{isSelected ? 'Sélectionné' : 'Ajouter'}</span>
</wcs-badge>
</div>
</div>
);
})}
</div>
)}
</div>
)}
</div>
)}
</div>
)}
</div>
</wcs-card-body>
</wcs-card>
)}
</div>
{/* Bottom Action Bar */}
<div className="form-sticky-bottom-bar">
<label className={`premium-checkbox-label ${actif ? 'selected' : ''}`}>
<input type="checkbox" checked={actif} onChange={(e) => setActif(e.target.checked)} />
<span style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<wcs-badge className={actif ? "wcs-success" : "wcs-secondary"} shape="round">
<wcs-mat-icon icon={actif ? "visibility" : "visibility_off"} style={{ fontSize: '13px', marginRight: '4px' }}></wcs-mat-icon>
{actif ? 'Chantier Actif et visible' : 'Chantier Inactif / Masqué'}
</wcs-badge>
</span>
</label>
{/* Barre d'action inférieure sticky avec Switch d'activation et WCS buttons */}
<div className="form-sticky-bottom-bar d-flex justify-content-between align-items-center p-3 rounded shadow-sm bg-white border mt-4">
<div className="d-flex align-items-center gap-3">
<label className="switch-container">
<input
type="checkbox"
checked={actif}
onChange={(e) => setActif(e.target.checked)}
/>
<span className="switch-slider"></span>
</label>
<div>
<strong className="d-block">{actif ? 'Chantier Actif et visible' : 'Chantier Inactif / Masqué'}</strong>
<small className="text-muted">{actif ? 'Ce chantier sera diffusé aux usagers et aux agents' : 'Ce chantier restera en brouillon non diffusé'}</small>
</div>
</div>
<div className="bottom-bar-btns">
<div className="d-flex gap-2">
<wcs-button className="wcs-secondary" mode="stroked" onClick={() => navigate('/travaux')}>
Annuler
<span>Annuler</span>
</wcs-button>
<wcs-button
className="wcs-primary"
<wcs-button
className="wcs-primary"
onClick={handleCreate}
disabled={saving || !canCreateTravaux}
loading={saving}
>
{saving ? (
<>
<wcs-spinner mode="border" style={{ width: '16px', height: '16px', marginRight: '6px' }}></wcs-spinner>
<span>Enregistrement...</span>
</>
) : (
<>
<wcs-mat-icon icon="check" style={{ marginRight: '6px' }}></wcs-mat-icon>
<span>Enregistrer les travaux</span>
</>
)}
<wcs-mat-icon icon="check" size="s" class="mr-1"></wcs-mat-icon>
<span>Enregistrer le chantier</span>
</wcs-button>
</div>
</div>
{/* WCS Footer Corporate SNCF */}
<wcs-footer className="sncf-page-footer mt-5">
<div className="d-flex align-items-center gap-3">
<div className="footer-sncf-badge">
<wcs-mat-icon icon="construction" style={{ fontSize: '20px', color: '#ffffff' }}></wcs-mat-icon>
</div>
<div>
<strong style={{ color: '#ffffff', fontSize: '14px', display: 'block' }}>
FERROVIA PANEL · DIRECTION TECHNIQUE & INFRASTRUCTURE SNCF
</strong>
<span style={{ color: 'rgba(255, 255, 255, 0.75)', fontSize: '12px' }}>
Module Officiel de Planification des Travaux & Substitutions Routières
</span>
</div>
</div>
<div slot="end-left">
<span style={{ fontSize: '12px', color: 'rgba(255, 255, 255, 0.65)' }}>
© {new Date().getFullYear()} SNCF · Réseau Ferroviaire National
</span>
</div>
<div slot="end-right">
<div className="d-flex align-items-center gap-2">
<wcs-badge className="wcs-success" shape="rounded">
<wcs-mat-icon icon="cloud_done" class="mr-1"></wcs-mat-icon>
Supabase Connecté
</wcs-badge>
<span style={{ fontSize: '12px', color: 'rgba(255, 255, 255, 0.7)' }}>
v2.6.0-WCS
</span>
</div>
</div>
</wcs-footer>
</div>
</div>
</div>