diff --git a/.env b/.env index a82982d..3e9e543 100644 --- a/.env +++ b/.env @@ -1,2 +1,6 @@ NEXT_PUBLIC_SUPABASE_URL=https://supabase-ferrovia.mrpatator.fr -NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_6dB95QNDKiHcFD81TJETqq_3Mu5rWWU \ No newline at end of file +NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_k8EI4S2YA_6Hl5av8uCBr3_S1Bj5g_F + +REACT_APP_SUPABASE_URL=https://supabase-ferrovia.mrpatator.fr +REACT_APP_SUPABASE_ANON_KEY=sb_publishable_k8EI4S2YA_6Hl5av8uCBr3_S1Bj5g_F +REACT_APP_SUPABASE_PUBLISHABLE_KEY=sb_publishable_k8EI4S2YA_6Hl5av8uCBr3_S1Bj5g_F \ No newline at end of file diff --git a/.env.local b/.env.local index a82982d..3e9e543 100644 --- a/.env.local +++ b/.env.local @@ -1,2 +1,6 @@ NEXT_PUBLIC_SUPABASE_URL=https://supabase-ferrovia.mrpatator.fr -NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_6dB95QNDKiHcFD81TJETqq_3Mu5rWWU \ No newline at end of file +NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_k8EI4S2YA_6Hl5av8uCBr3_S1Bj5g_F + +REACT_APP_SUPABASE_URL=https://supabase-ferrovia.mrpatator.fr +REACT_APP_SUPABASE_ANON_KEY=sb_publishable_k8EI4S2YA_6Hl5av8uCBr3_S1Bj5g_F +REACT_APP_SUPABASE_PUBLISHABLE_KEY=sb_publishable_k8EI4S2YA_6Hl5av8uCBr3_S1Bj5g_F \ No newline at end of file diff --git a/SCHEME.sql b/SCHEME.sql new file mode 100644 index 0000000..aa64900 --- /dev/null +++ b/SCHEME.sql @@ -0,0 +1,1607 @@ +-- ============================================================================ +-- FERROVIA PANEL — SCHÉMA COMPLET ET UNIFIÉ DE LA BASE DE DONNÉES +-- Fichier : SCHEME.sql (Racine du projet) +-- Compatible : Supabase PostgreSQL (Cloud & Self-Hosted), Supabase Auth (GoTrue), Supabase Studio +-- Date de révision : 2026-10-03 +-- ============================================================================ +-- Ce script SQL contient l'intégralité de l'infrastructure de la base de données : +-- 1. Extensions (pgcrypto, uuid-ossp) +-- 2. Types ENUM (service_type, station_type, transport_type) +-- 3. Fonctions utilitaires & Triggers updated_at +-- 4. Fonction d'audit (audit_logs) +-- 5. Les 37 Tables complètes avec types exacts et contraintes +-- 6. Index de performance (B-Tree, GIN JSONB, clés étrangères) +-- 7. Synchronisation native Supabase Auth (auth.users <-> public.utilisateurs) +-- 8. Fonctions RPC métier (authenticate_user, get_gare_details, etc.) +-- 9. Politiques de sécurité RLS (Row Level Security) +-- 10. Configuration des Buckets Supabase Storage (fiches-horaires, materiel-roulant, banque-annonces) +-- 11. Privilèges & Grants (anon, authenticated, service_role) +-- 12. Données initiales (Comptes Administrateurs, Paramètres, Types de train) +-- ============================================================================ + +-- ============================================================================ +-- 1. EXTENSIONS POSTGRESQL +-- ============================================================================ +CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA extensions; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; + +-- Rendre les fonctions accessibles sans préfixe +SET search_path = public, extensions, auth, pg_temp; + +-- ============================================================================ +-- 2. TYPES ENUM +-- ============================================================================ +DO $$ BEGIN + CREATE TYPE "public"."service_type" AS ENUM ('TER', 'TGV', 'Intercités', 'Fret'); +EXCEPTION WHEN duplicate_object THEN NULL; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."station_type" AS ENUM ('interurbaine', 'ville'); +EXCEPTION WHEN duplicate_object THEN NULL; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."transport_type" AS ENUM ('bus', 'tram', 'metro', 'tram-train', 'train'); +EXCEPTION WHEN duplicate_object THEN NULL; +END $$; + +-- ============================================================================ +-- 3. FONCTIONS UTILITAIRES & TRIGGERS UPDATED_AT +-- ============================================================================ +CREATE OR REPLACE FUNCTION "public"."set_updated_at"() +RETURNS trigger +LANGUAGE plpgsql AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$; + +CREATE OR REPLACE FUNCTION "public"."handle_updated_at"() +RETURNS trigger +LANGUAGE plpgsql AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$; + +CREATE OR REPLACE FUNCTION "public"."update_updated_at_column"() +RETURNS trigger +LANGUAGE plpgsql AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$; + +CREATE OR REPLACE FUNCTION "public"."update_travaux_updated_at"() +RETURNS trigger +LANGUAGE plpgsql AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$; + +CREATE OR REPLACE FUNCTION "public"."update_utilisateurs_updated_at"() +RETURNS trigger +LANGUAGE plpgsql AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$; + +-- Stub RPC GTFS +CREATE OR REPLACE FUNCTION "public"."import_gtfs_rpc"(payload json) +RETURNS json +LANGUAGE plpgsql AS $$ +BEGIN + RETURN json_build_object('status', 'ok', 'created', 0, 'warnings', json_build_array()); +EXCEPTION WHEN OTHERS THEN + RETURN json_build_object('status', 'error', 'message', SQLERRM); +END; +$$; + +-- ============================================================================ +-- 4. FONCTION D'AUDIT +-- ============================================================================ +CREATE OR REPLACE FUNCTION "public"."log_audit"() +RETURNS trigger +LANGUAGE plpgsql SECURITY DEFINER +SET search_path = public, extensions, auth, pg_temp +AS $$ +DECLARE + v_changed_by_text text; + v_changed_by uuid; +BEGIN + -- Tenter de récupérer l'UUID de l'utilisateur connecté via JWT + v_changed_by_text := COALESCE( + current_setting('request.jwt.claim.sub', true), + current_setting('jwt.claims.user_id', true), + current_setting('jwt.claims.sub', true) + ); + + IF v_changed_by_text IS NOT NULL AND v_changed_by_text <> '' THEN + BEGIN + v_changed_by := v_changed_by_text::uuid; + EXCEPTION WHEN others THEN + v_changed_by := NULL; + END; + ELSE + v_changed_by := NULL; + END IF; + + IF (TG_OP = 'INSERT') THEN + INSERT INTO public.audit_logs(table_name, operation, record_id, changed_by, new_data, changed_at) + VALUES (TG_TABLE_NAME, 'INSERT', NEW.id, v_changed_by, row_to_json(NEW)::jsonb, NOW()); + RETURN NEW; + ELSIF (TG_OP = 'UPDATE') THEN + INSERT INTO public.audit_logs(table_name, operation, record_id, changed_by, old_data, new_data, changed_at) + VALUES (TG_TABLE_NAME, 'UPDATE', NEW.id, v_changed_by, row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb, NOW()); + RETURN NEW; + ELSIF (TG_OP = 'DELETE') THEN + INSERT INTO public.audit_logs(table_name, operation, record_id, changed_by, old_data, changed_at) + VALUES (TG_TABLE_NAME, 'DELETE', OLD.id, v_changed_by, row_to_json(OLD)::jsonb, NOW()); + RETURN OLD; + END IF; + RETURN NULL; +EXCEPTION WHEN OTHERS THEN + -- Ne jamais bloquer la transaction principale en cas d'erreur de journalisation + RETURN COALESCE(NEW, OLD); +END; +$$; + +-- ============================================================================ +-- 5. TABLES (ORDONNÉES PAR DÉPENDANCES) +-- ============================================================================ + +-- ---------------------------------------------------------------------------- +-- 5.01 audit_logs +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."audit_logs" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "table_name" text NOT NULL, + "operation" text NOT NULL, + "record_id" uuid, + "changed_by" uuid, + "old_data" jsonb, + "new_data" jsonb, + "changed_at" timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_audit_logs_table" ON "public"."audit_logs" ("table_name"); +CREATE INDEX IF NOT EXISTS "idx_audit_logs_record" ON "public"."audit_logs" ("record_id"); +CREATE INDEX IF NOT EXISTS "idx_audit_logs_changed_by" ON "public"."audit_logs" ("changed_by"); +CREATE INDEX IF NOT EXISTS "idx_audit_logs_changed_at" ON "public"."audit_logs" ("changed_at" DESC); + +-- ---------------------------------------------------------------------------- +-- 5.02 utilisateurs (Profils métier et administrateurs du panel) +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."utilisateurs" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "email" varchar(255) NOT NULL UNIQUE, + "nom" varchar(100) NOT NULL DEFAULT '', + "prenom" varchar(100) NOT NULL DEFAULT '', + "role" varchar(50) NOT NULL DEFAULT 'utilisateur', + "actif" boolean NOT NULL DEFAULT true, + "password_hash" varchar(255), + "photo_url" text, + "region_admin" boolean DEFAULT false, + "region_id" varchar(100), + "region_nom" varchar(255), + "permissions" jsonb NOT NULL DEFAULT '{}'::jsonb, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE UNIQUE INDEX IF NOT EXISTS "utilisateurs_email_lower_idx" ON "public"."utilisateurs" (lower(email::text)); +CREATE INDEX IF NOT EXISTS "idx_utilisateurs_email" ON "public"."utilisateurs" ("email"); +CREATE INDEX IF NOT EXISTS "idx_utilisateurs_role" ON "public"."utilisateurs" ("role"); +CREATE INDEX IF NOT EXISTS "idx_utilisateurs_actif" ON "public"."utilisateurs" ("actif"); +CREATE INDEX IF NOT EXISTS "idx_utilisateurs_region_id" ON "public"."utilisateurs" ("region_id"); + +-- ---------------------------------------------------------------------------- +-- 5.03 profils_utilisateurs (Voyageurs / App mobile & site public) +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."profils_utilisateurs" ( + "id" uuid NOT NULL PRIMARY KEY, + "email" text NOT NULL, + "prenom" text, + "nom" text, + "date_naissance" date, + "telephone" text, + "carte_reduction" text, + "gares_favorites" text[] DEFAULT '{}'::text[], + "lignes_favorites" text[] DEFAULT '{}'::text[], + "cartes_bancaires" jsonb DEFAULT '[]'::jsonb, + "mode_paiement_favori" text, + "commandes" jsonb DEFAULT '[]'::jsonb, + "panier" jsonb DEFAULT '[]'::jsonb, + "trajets_favoris" text[] DEFAULT '{}'::text[], + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_profils_email" ON "public"."profils_utilisateurs" ("email"); + +-- ---------------------------------------------------------------------------- +-- 5.04 gares +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."gares" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "nom" text NOT NULL, + "type" varchar(50) NOT NULL DEFAULT 'ville', + "services" "public"."service_type"[] NOT NULL DEFAULT '{}'::service_type[], + "informations" jsonb DEFAULT '{}'::jsonb, + "quais_hall" jsonb DEFAULT '[]'::jsonb, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_gares_nom" ON "public"."gares" ("nom"); +CREATE INDEX IF NOT EXISTS "idx_gares_type" ON "public"."gares" ("type"); + +-- ---------------------------------------------------------------------------- +-- 5.05 gare_quais +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."gare_quais" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE, + "nom" text NOT NULL, + "distance_metres" integer NOT NULL DEFAULT 0, + "created_at" timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_gare_quais_gare_id" ON "public"."gare_quais" ("gare_id"); + +-- ---------------------------------------------------------------------------- +-- 5.06 gare_transports +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."gare_transports" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE, + "type" "public"."transport_type" NOT NULL, + "ligne" text, + "created_at" timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_gare_transports_gare_id" ON "public"."gare_transports" ("gare_id"); + +-- ---------------------------------------------------------------------------- +-- 5.07 types_train +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."types_train" ( + "id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "nom" varchar(100) NOT NULL, + "code" varchar(20), + "description" text, + "logo_url" text, + "logo_color_url" text, + "couleur" varchar(50), + "type_train" varchar(100), + "color" varchar(50), + "actif" boolean DEFAULT true, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_types_train_nom" ON "public"."types_train" ("nom"); +CREATE INDEX IF NOT EXISTS "idx_types_train_actif" ON "public"."types_train" ("actif"); + +-- ---------------------------------------------------------------------------- +-- 5.08 depots_technicentres (Dépôts, Technicentres et Ateliers de Maintenance) +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."depots_technicentres" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "nom" varchar(255) NOT NULL, + "code" varchar(50) UNIQUE, + "ville" varchar(100) NOT NULL, + "gare_proche" varchar(255), + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "capacite_max" integer NOT NULL DEFAULT 20, + "voies_disponibles" integer, + "specialites" jsonb DEFAULT '[]'::jsonb, + "region_id" varchar(100), + "region_nom" varchar(255), + "adresse" text, + "description" text, + "actif" boolean NOT NULL DEFAULT true, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_depots_nom" ON "public"."depots_technicentres" ("nom"); +CREATE INDEX IF NOT EXISTS "idx_depots_code" ON "public"."depots_technicentres" ("code"); +CREATE INDEX IF NOT EXISTS "idx_depots_ville" ON "public"."depots_technicentres" ("ville"); +CREATE INDEX IF NOT EXISTS "idx_depots_actif" ON "public"."depots_technicentres" ("actif") WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS "idx_depots_region" ON "public"."depots_technicentres" ("region_id"); + +-- ---------------------------------------------------------------------------- +-- 5.09 "materiel-roulant" +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."materiel-roulant" ( + "id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "nom" varchar(255) NOT NULL, + "nom_technique" varchar(255), + "capacite" integer, + "type_train" varchar(100), + "numero_serie" varchar(100), + "image_url" text, + "images" jsonb DEFAULT '[]'::jsonb, + "serie" varchar(100), + "type" varchar(100), + "places_assises" integer, + "vitesse_max" integer, + "alimentation" varchar(100), + "photo_url" text, + "description" text, + "actif" boolean DEFAULT true, + "depot_id" uuid REFERENCES "public"."depots_technicentres"("id") ON DELETE SET NULL, + "depot_attache" varchar(255), + "statut_maintenance" varchar(100) DEFAULT 'OPERATIONNEL', + "date_prochaine_maintenance" date, + "kilometrage" integer DEFAULT 0, + "couplage_famille" varchar(100), + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); + +-- Migration idempotente pour tables déjà créées +DO $$ +BEGIN + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "nom_technique" varchar(255); + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "capacite" integer; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "type_train" varchar(100); + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "numero_serie" varchar(100); + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "image_url" text; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "images" jsonb DEFAULT '[]'::jsonb; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "places_assises" integer; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "vitesse_max" integer; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "alimentation" varchar(100); + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "photo_url" text; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "description" text; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "actif" boolean DEFAULT true; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "depot_id" uuid REFERENCES "public"."depots_technicentres"("id") ON DELETE SET NULL; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "depot_attache" varchar(255); + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "statut_maintenance" varchar(100) DEFAULT 'OPERATIONNEL'; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "date_prochaine_maintenance" date; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "kilometrage" integer DEFAULT 0; + ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "couplage_famille" varchar(100); +EXCEPTION WHEN OTHERS THEN + NULL; +END $$; + +CREATE INDEX IF NOT EXISTS "idx_materiel_serie" ON "public"."materiel-roulant" ("serie"); +CREATE INDEX IF NOT EXISTS "idx_materiel_actif" ON "public"."materiel-roulant" ("actif"); +CREATE INDEX IF NOT EXISTS "idx_materiel_type_train" ON "public"."materiel-roulant" ("type_train"); +CREATE INDEX IF NOT EXISTS "idx_materiel_numero_serie" ON "public"."materiel-roulant" ("numero_serie"); +CREATE INDEX IF NOT EXISTS "idx_materiel_depot_id" ON "public"."materiel-roulant" ("depot_id"); +CREATE INDEX IF NOT EXISTS "idx_materiel_depot_attache" ON "public"."materiel-roulant" ("depot_attache"); +CREATE INDEX IF NOT EXISTS "idx_materiel_statut_maintenance" ON "public"."materiel-roulant" ("statut_maintenance"); + +-- ---------------------------------------------------------------------------- +-- 5.09 services_annuels +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."services_annuels" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "nom" varchar(255) NOT NULL, + "code" varchar(50), + "date_debut" date NOT NULL, + "date_fin" date NOT NULL, + "description" text, + "actif" boolean DEFAULT false, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_services_annuels_dates" ON "public"."services_annuels" ("date_debut", "date_fin"); +CREATE INDEX IF NOT EXISTS "idx_services_annuels_actif" ON "public"."services_annuels" ("actif") WHERE deleted_at IS NULL; + +-- ---------------------------------------------------------------------------- +-- 5.10 lignes +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."lignes" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "nom" varchar(255) NOT NULL, + "code" varchar(50), + "gare_depart_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "gare_arrivee_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "actif" boolean DEFAULT true, + "fh_url" text, + "region" varchar(100), + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_lignes_actif" ON "public"."lignes" ("actif") WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS "idx_lignes_fh_url" ON "public"."lignes" ("fh_url"); +CREATE INDEX IF NOT EXISTS "idx_lignes_region" ON "public"."lignes" ("region"); + +-- ---------------------------------------------------------------------------- +-- 5.11 lignes_gares +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."lignes_gares" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "ligne_id" uuid NOT NULL REFERENCES "public"."lignes"("id") ON DELETE CASCADE, + "gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE, + "ordre" integer NOT NULL DEFAULT 1, + "desserte" varchar(50) DEFAULT 'tous', + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_lignes_gares_ligne_id" ON "public"."lignes_gares" ("ligne_id"); +CREATE INDEX IF NOT EXISTS "idx_lignes_gares_gare_id" ON "public"."lignes_gares" ("gare_id"); +CREATE INDEX IF NOT EXISTS "idx_lignes_gares_ordre" ON "public"."lignes_gares" ("ligne_id", "ordre"); + +-- ---------------------------------------------------------------------------- +-- 5.12 horaires (Circulations principales) +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."horaires" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "numero_train" varchar(50) NOT NULL, + "nom_train" varchar(255), + "code_mission" varchar(50), + "sens" varchar(20) DEFAULT 'aller', + "type_train" varchar(100), + "type_train_id" bigint REFERENCES "public"."types_train"("id") ON DELETE SET NULL, + "transporteur" varchar(100) DEFAULT 'SNCF Voyageurs', + "gare_depart_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "gare_depart_nom" varchar(255) NOT NULL, + "heure_depart" time, + "gare_arrivee_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "gare_arrivee_nom" varchar(255) NOT NULL, + "heure_arrivee" time, + "gares_desservies" jsonb NOT NULL DEFAULT '[]'::jsonb, + "attributions_quais" jsonb NOT NULL DEFAULT '{}'::jsonb, + "snapshot_gares_desservies" jsonb DEFAULT '[]'::jsonb, + "circule_lundi" boolean DEFAULT true, + "circule_mardi" boolean DEFAULT true, + "circule_mercredi" boolean DEFAULT true, + "circule_jeudi" boolean DEFAULT true, + "circule_vendredi" boolean DEFAULT true, + "circule_samedi" boolean DEFAULT true, + "circule_dimanche" boolean DEFAULT true, + "circule_jours_feries" boolean DEFAULT false, + "circule_dimanches_feries" boolean DEFAULT false, + "date_debut" timestamptz, + "date_fin" timestamptz, + "jours_personnalises" jsonb NOT NULL DEFAULT '[]'::jsonb, + "jours_non_circulation" jsonb NOT NULL DEFAULT '[]'::jsonb, + "calendrier_circulation" jsonb NOT NULL DEFAULT '[]'::jsonb, + "periode_validite" jsonb NOT NULL DEFAULT '{}'::jsonb, + "materiel_roulant_id" bigint REFERENCES "public"."materiel-roulant"("id") ON DELETE SET NULL, + "composition_train" jsonb NOT NULL DEFAULT '[]'::jsonb, + "ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL, + "ligne_nom" varchar(255), + "region_id" varchar(100), + "region" varchar(255), + "service_annuel_id" uuid REFERENCES "public"."services_annuels"("id") ON DELETE SET NULL, + "service_annuel_nom" varchar(255), + "service_type" varchar(50) DEFAULT 'TER', + "est_substitution" boolean NOT NULL DEFAULT false, + "motif_substitution" varchar(255), + "substitution_disponible" boolean NOT NULL DEFAULT false, + "horaire_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL, + "substitutions_associees" jsonb NOT NULL DEFAULT '[]'::jsonb, + "sillon_id" text, + "tranche" varchar(50), + "gestionnaire_infrastructure" varchar(100) DEFAULT 'SNCF Réseau', + "vitesse_max_kmh" integer, + "statut" varchar(50) NOT NULL DEFAULT 'a_l_heure', + "retard_minutes" integer NOT NULL DEFAULT 0, + "retard_motif" text, + "informations_voyageurs" text, + "actif" boolean NOT NULL DEFAULT true, + "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb, + "notes" text, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_horaires_numero_train" ON "public"."horaires" ("numero_train"); +CREATE INDEX IF NOT EXISTS "idx_horaires_gare_depart" ON "public"."horaires" ("gare_depart_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_gare_arrivee" ON "public"."horaires" ("gare_arrivee_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_departs" ON "public"."horaires" ("gare_depart_id", "heure_depart"); +CREATE INDEX IF NOT EXISTS "idx_horaires_arrivees" ON "public"."horaires" ("gare_arrivee_id", "heure_arrivee"); +CREATE INDEX IF NOT EXISTS "idx_horaires_actif" ON "public"."horaires" ("actif") WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS "idx_horaires_type_train" ON "public"."horaires" ("type_train_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_materiel" ON "public"."horaires" ("materiel_roulant_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_ligne" ON "public"."horaires" ("ligne_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_service_annuel" ON "public"."horaires" ("service_annuel_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_substitution" ON "public"."horaires" ("est_substitution"); +CREATE INDEX IF NOT EXISTS "idx_horaires_region_id" ON "public"."horaires" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_dates" ON "public"."horaires" ("date_debut", "date_fin"); +CREATE INDEX IF NOT EXISTS "idx_horaires_statut" ON "public"."horaires" ("statut"); +CREATE INDEX IF NOT EXISTS "idx_horaires_gares_desservies_gin" ON "public"."horaires" USING gin ("gares_desservies" jsonb_path_ops); +CREATE INDEX IF NOT EXISTS "idx_horaires_attributions_quais_gin" ON "public"."horaires" USING gin ("attributions_quais"); +CREATE INDEX IF NOT EXISTS "idx_horaires_calendrier_gin" ON "public"."horaires" USING gin ("calendrier_circulation" jsonb_path_ops); +CREATE INDEX IF NOT EXISTS "idx_horaires_metadata_gin" ON "public"."horaires" USING gin ("metadata"); + +DO $$ +BEGIN + ALTER TABLE "public"."horaires" ADD COLUMN IF NOT EXISTS "couplage_train_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL; + ALTER TABLE "public"."horaires" ADD COLUMN IF NOT EXISTS "couplage_numero_train" varchar(50); + ALTER TABLE "public"."horaires" ADD COLUMN IF NOT EXISTS "manoeuvres" jsonb DEFAULT '[]'::jsonb; +EXCEPTION WHEN OTHERS THEN + NULL; +END $$; + +-- ---------------------------------------------------------------------------- +-- 5.13 horaires_gares_desservies +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."horaires_gares_desservies" ( + "id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "horaire_id" uuid REFERENCES "public"."horaires"("id") ON DELETE CASCADE, + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "gare_nom" varchar(255) NOT NULL, + "heure_arrivee" time, + "heure_depart" time, + "ordre" integer NOT NULL DEFAULT 0, + "quai" varchar(50), + "quai_id" uuid REFERENCES "public"."gare_quais"("id") ON DELETE SET NULL, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_gares_desservies_horaire" ON "public"."horaires_gares_desservies" ("horaire_id"); +CREATE INDEX IF NOT EXISTS "idx_gares_desservies_ordre" ON "public"."horaires_gares_desservies" ("horaire_id", "ordre"); +CREATE INDEX IF NOT EXISTS "idx_hgd_gare_id" ON "public"."horaires_gares_desservies" ("gare_id"); + +-- ---------------------------------------------------------------------------- +-- 5.14 horaires_substitutions +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."horaires_substitutions" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "horaire_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL, + "region_id" varchar(100), + "region" varchar(255), + "numero_train" varchar(50), + "type_train" varchar(100), + "type_train_id" bigint REFERENCES "public"."types_train"("id") ON DELETE SET NULL, + "gare_depart_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "gare_depart_nom" varchar(255), + "heure_depart" time, + "gare_arrivee_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "gare_arrivee_nom" varchar(255), + "heure_arrivee" time, + "date_debut" timestamptz, + "date_fin" timestamptz, + "circule_lundi" boolean DEFAULT true, + "circule_mardi" boolean DEFAULT true, + "circule_mercredi" boolean DEFAULT true, + "circule_jeudi" boolean DEFAULT true, + "circule_vendredi" boolean DEFAULT true, + "circule_samedi" boolean DEFAULT true, + "circule_dimanche" boolean DEFAULT true, + "circule_jours_feries" boolean DEFAULT false, + "jours_personnalises" jsonb DEFAULT '[]'::jsonb, + "jours_non_circulation" jsonb DEFAULT '[]'::jsonb, + "materiel_roulant_id" bigint REFERENCES "public"."materiel-roulant"("id") ON DELETE SET NULL, + "ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL, + "ligne_nom" varchar(255), + "service_annuel_id" uuid REFERENCES "public"."services_annuels"("id") ON DELETE SET NULL, + "service_annuel_nom" varchar(255), + "est_substitution" boolean DEFAULT true, + "motif_substitution" varchar(255), + "gares_desservies" jsonb DEFAULT '[]'::jsonb, + "composition_train" jsonb DEFAULT '[]'::jsonb, + "actif" boolean DEFAULT true, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_horaires_substitutions_region" ON "public"."horaires_substitutions" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_substitutions_horaire" ON "public"."horaires_substitutions" ("horaire_id"); +CREATE INDEX IF NOT EXISTS "idx_horaires_substitutions_actif" ON "public"."horaires_substitutions" ("actif") WHERE deleted_at IS NULL; + +-- ---------------------------------------------------------------------------- +-- 5.15 perturbations +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."perturbations" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "horaire_id" uuid NOT NULL REFERENCES "public"."horaires"("id") ON DELETE CASCADE, + "type_perturbation" varchar(50) NOT NULL CHECK (type_perturbation IN ('retard','suppression','modification_parcours')), + "duree_retard_minutes" integer, + "cause" varchar(100) NOT NULL CHECK (cause IN ('incident_technique','accident_personne','conditions_meteorologiques','travaux','mouvement_social','intervention_police','affluence','malveillance','autre')), + "cause_details" text, + "sillon_substitution_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL, + "date_debut" timestamptz NOT NULL DEFAULT now(), + "date_fin" timestamptz, + "actif" boolean DEFAULT true, + "notes" text, + "region_id" varchar(100), + "created_by" uuid, + "perturbations_arrets" jsonb, + "jours_perturbation" jsonb, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_perturbations_horaire" ON "public"."perturbations" ("horaire_id"); +CREATE INDEX IF NOT EXISTS "idx_perturbations_type" ON "public"."perturbations" ("type_perturbation"); +CREATE INDEX IF NOT EXISTS "idx_perturbations_actif" ON "public"."perturbations" ("actif") WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS "idx_perturbations_dates" ON "public"."perturbations" ("date_debut","date_fin"); +CREATE INDEX IF NOT EXISTS "idx_perturbations_sillon_substitution" ON "public"."perturbations" ("sillon_substitution_id"); +CREATE INDEX IF NOT EXISTS "idx_perturbations_region_id" ON "public"."perturbations" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_perturbations_created_by" ON "public"."perturbations" ("created_by"); + +-- ---------------------------------------------------------------------------- +-- 5.16 perturbations_modifications_parcours +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."perturbations_modifications_parcours" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "perturbation_id" uuid NOT NULL REFERENCES "public"."perturbations"("id") ON DELETE CASCADE, + "type_modification" varchar(20) NOT NULL CHECK (type_modification IN ('gare_supprimee','gare_ajoutee','horaire_modifie')), + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "gare_nom" varchar(255) NOT NULL, + "nouvelle_heure_arrivee" time, + "nouvelle_heure_depart" time, + "ordre" integer, + "created_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_modif_parcours_perturbation" ON "public"."perturbations_modifications_parcours" ("perturbation_id"); +CREATE INDEX IF NOT EXISTS "idx_modif_parcours_gare" ON "public"."perturbations_modifications_parcours" ("gare_id"); + +-- ---------------------------------------------------------------------------- +-- 5.17 info_trafic +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."info_trafic" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" varchar(255) NOT NULL, + "description" text, + "type" varchar(50) NOT NULL CHECK (type IN ('perturbation','travaux','greve','meteo','autre')), + "severite" varchar(20) NOT NULL CHECK (severite IN ('info','warning','critical')), + "ligne" varchar(100), + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "date_debut" timestamptz NOT NULL DEFAULT now(), + "date_fin" timestamptz, + "actif" boolean DEFAULT true, + "region_id" varchar(100), + "created_by" uuid, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_info_trafic_actif" ON "public"."info_trafic" ("actif") WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS "idx_info_trafic_dates" ON "public"."info_trafic" ("date_debut","date_fin"); +CREATE INDEX IF NOT EXISTS "idx_info_trafic_region_id" ON "public"."info_trafic" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_info_trafic_created_by" ON "public"."info_trafic" ("created_by"); + +-- ---------------------------------------------------------------------------- +-- 5.18 evenements +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."evenements" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" varchar(255) NOT NULL, + "description" text, + "lieu" varchar(255), + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "date_debut" timestamptz NOT NULL, + "date_fin" timestamptz, + "image_url" varchar(500), + "lien_externe" varchar(500), + "actif" boolean DEFAULT true, + "visible_site" boolean DEFAULT true, + "region_id" varchar(100), + "created_by" uuid, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_evenements_actif" ON "public"."evenements" ("actif") WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS "idx_evenements_dates" ON "public"."evenements" ("date_debut","date_fin"); +CREATE INDEX IF NOT EXISTS "idx_evenements_region_id" ON "public"."evenements" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_evenements_created_by" ON "public"."evenements" ("created_by"); + +-- ---------------------------------------------------------------------------- +-- 5.19 actualites +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."actualites" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" varchar(255) NOT NULL, + "contenu" text NOT NULL, + "resume" varchar(500), + "categorie" varchar(50) CHECK (categorie IN ('general','service','promotion','partenariat','autre')), + "image_url" varchar(500), + "image_couverture" text, + "date_publication" timestamptz NOT NULL DEFAULT now(), + "auteur" varchar(100), + "actif" boolean DEFAULT true, + "mise_en_avant" boolean DEFAULT false, + "region_id" varchar(100), + "created_by" uuid, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_actualites_actif" ON "public"."actualites" ("actif") WHERE deleted_at IS NULL; +CREATE INDEX IF NOT EXISTS "idx_actualites_publication" ON "public"."actualites" ("date_publication"); +CREATE INDEX IF NOT EXISTS "idx_actualites_region_id" ON "public"."actualites" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_actualites_created_by" ON "public"."actualites" ("created_by"); + +-- ---------------------------------------------------------------------------- +-- 5.20 travaux +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."travaux" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" text NOT NULL, + "description" text, + "date_debut" date NOT NULL, + "date_fin" date NOT NULL, + "jours_travaux" text[] NOT NULL, + "heure_debut" time NOT NULL, + "heure_fin" time NOT NULL, + "actif" boolean DEFAULT true, + "ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL, + "ligne_nom" text, + "region_id" varchar(100), + "created_by" uuid REFERENCES "public"."utilisateurs"("id") ON DELETE SET NULL, + "substitutions_attribuees" jsonb DEFAULT '[]'::jsonb, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz, + CONSTRAINT "valid_dates" CHECK (date_fin >= date_debut) +); +CREATE INDEX IF NOT EXISTS "idx_travaux_dates" ON "public"."travaux" ("date_debut","date_fin"); +CREATE INDEX IF NOT EXISTS "idx_travaux_actif" ON "public"."travaux" ("actif"); +CREATE INDEX IF NOT EXISTS "idx_travaux_deleted" ON "public"."travaux" ("deleted_at"); +CREATE INDEX IF NOT EXISTS "idx_travaux_ligne_id" ON "public"."travaux" ("ligne_id"); +CREATE INDEX IF NOT EXISTS "idx_travaux_region_id" ON "public"."travaux" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_travaux_created_by" ON "public"."travaux" ("created_by"); +CREATE INDEX IF NOT EXISTS "idx_travaux_substitutions_attribuees_gin" ON "public"."travaux" USING gin ("substitutions_attribuees"); + +-- ---------------------------------------------------------------------------- +-- 5.21 travaux_substitutions +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."travaux_substitutions" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "travaux_id" uuid NOT NULL REFERENCES "public"."travaux"("id") ON DELETE CASCADE, + "horaire_id" uuid NOT NULL REFERENCES "public"."horaires"("id") ON DELETE CASCADE, + "ordre" integer DEFAULT 0, + "created_at" timestamptz DEFAULT now(), + UNIQUE ("travaux_id","horaire_id") +); +CREATE INDEX IF NOT EXISTS "idx_travaux_substitutions_travaux" ON "public"."travaux_substitutions" ("travaux_id"); +CREATE INDEX IF NOT EXISTS "idx_travaux_substitutions_horaire" ON "public"."travaux_substitutions" ("horaire_id"); + +-- ---------------------------------------------------------------------------- +-- 5.22 infos_sillons +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."infos_sillons" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" varchar(255) NOT NULL, + "description" text, + "type" varchar(50) DEFAULT 'information', + "ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL, + "ligne_nom" varchar(255), + "travaux_id" uuid REFERENCES "public"."travaux"("id") ON DELETE SET NULL, + "date_debut" timestamptz, + "date_fin" timestamptz, + "actif" boolean DEFAULT true, + "auto_generated" boolean DEFAULT false, + "region_id" varchar(100), + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_infos_sillons_ligne_id" ON "public"."infos_sillons" ("ligne_id"); +CREATE INDEX IF NOT EXISTS "idx_infos_sillons_travaux_id" ON "public"."infos_sillons" ("travaux_id"); +CREATE INDEX IF NOT EXISTS "idx_infos_sillons_region_id" ON "public"."infos_sillons" ("region_id"); +CREATE INDEX IF NOT EXISTS "idx_infos_sillons_actif" ON "public"."infos_sillons" ("actif"); + +-- ---------------------------------------------------------------------------- +-- 5.23 fiches_horaires +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."fiches_horaires" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" varchar(255) NOT NULL, + "ligne_id" varchar(100), + "ligne_nom" varchar(255), + "code_ligne" varchar(50), + "service_annuel_id" varchar(100), + "service_annuel_nom" varchar(255), + "code_service_annuel" varchar(50), + "direction" varchar(50) DEFAULT 'aller', + "statut" varchar(50) DEFAULT 'Projet (Brouillon)', + "pdf_url" text, + "pdf_filename" varchar(255), + "pdf_storage_path" text, + "config" jsonb DEFAULT '{}'::jsonb, + "circulations_count" integer DEFAULT 0, + "gares_count" integer DEFAULT 0, + "region_id" varchar(100), + "created_by" varchar(255), + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_ligne_id" ON "public"."fiches_horaires" ("ligne_id"); +CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_service_id" ON "public"."fiches_horaires" ("service_annuel_id"); +CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_statut" ON "public"."fiches_horaires" ("statut"); +CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_region" ON "public"."fiches_horaires" ("region_id"); + +-- ---------------------------------------------------------------------------- +-- 5.24 tarifs +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."tarifs" ( + "id" integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "nom" varchar(255) NOT NULL, + "type" varchar(50) NOT NULL, + "description" text, + "prix_minimum" numeric(10,2), + "prix_par_km" numeric(10,3), + "age_maximum" integer, + "regions" text[] NOT NULL, + "date_debut" date, + "date_fin" date, + "ordre" integer NOT NULL DEFAULT 0, + "page_dediee" boolean DEFAULT false, + "infos" jsonb DEFAULT '{}'::jsonb, + "details" jsonb, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_tarifs_ordre" ON "public"."tarifs" ("ordre"); +CREATE INDEX IF NOT EXISTS "idx_tarifs_regions" ON "public"."tarifs" USING gin ("regions"); + +-- ---------------------------------------------------------------------------- +-- 5.25 parametres +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."parametres" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "cle" varchar(100) NOT NULL UNIQUE, + "valeur" jsonb, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_parametres_cle" ON "public"."parametres" ("cle"); + +-- ---------------------------------------------------------------------------- +-- 5.26 menus_regionaux +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."menus_regionaux" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "nom" varchar(200) NOT NULL, + "description" text, + "actif" boolean DEFAULT false, + "structure" jsonb DEFAULT '[]'::jsonb, + "region_id" varchar(64), + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_menus_regionaux_region_id" ON "public"."menus_regionaux" ("region_id"); + +-- ---------------------------------------------------------------------------- +-- 5.27 pages (CMS) +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."pages" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" text NOT NULL, + "slug" text, + "contenu" jsonb DEFAULT '[]'::jsonb, + "statut" text DEFAULT 'brouillon', + "region_id" varchar(255), + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_pages_slug" ON "public"."pages" ("slug"); +CREATE INDEX IF NOT EXISTS "idx_pages_region_id" ON "public"."pages" ("region_id"); + +-- ---------------------------------------------------------------------------- +-- 5.28 page_personnalisation +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."page_personnalisation" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "page" text NOT NULL UNIQUE, + "config" jsonb DEFAULT '{}'::jsonb, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); + +-- ---------------------------------------------------------------------------- +-- 5.29 info_gares +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."info_gares" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "titre" text, + "description" text, + "debut_affichage" timestamptz, + "fin_affichage" timestamptz, + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL, + "gare_ids" uuid[] DEFAULT '{}'::uuid[], + "ligne_ids" uuid[] DEFAULT '{}'::uuid[], + "type" text DEFAULT 'Information', + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_info_gares_gare_id" ON "public"."info_gares" ("gare_id"); +CREATE INDEX IF NOT EXISTS "idx_info_gares_ligne_id" ON "public"."info_gares" ("ligne_id"); +CREATE INDEX IF NOT EXISTS "idx_info_gares_gare_ids" ON "public"."info_gares" USING gin ("gare_ids"); +CREATE INDEX IF NOT EXISTS "idx_info_gares_ligne_ids" ON "public"."info_gares" USING gin ("ligne_ids"); + +-- ---------------------------------------------------------------------------- +-- 5.30 regles_quais +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."regles_quais" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE, + "quai_id" uuid NOT NULL REFERENCES "public"."gare_quais"("id") ON DELETE CASCADE, + "type_train" varchar(50), + "sens" varchar(20), + "priorite" integer DEFAULT 1, + "actif" boolean DEFAULT true, + "description" text, + "created_at" timestamptz DEFAULT now(), + "updated_at" timestamptz DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_regles_quais_gare" ON "public"."regles_quais" ("gare_id"); +CREATE INDEX IF NOT EXISTS "idx_regles_quais_quai" ON "public"."regles_quais" ("quai_id"); + +-- ---------------------------------------------------------------------------- +-- 5.31 messagerie_threads +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."messagerie_threads" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now() +); + +-- ---------------------------------------------------------------------------- +-- 5.32 messagerie_participants +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."messagerie_participants" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "thread_id" uuid NOT NULL REFERENCES "public"."messagerie_threads"("id") ON DELETE CASCADE, + "user_id" uuid NOT NULL REFERENCES "public"."utilisateurs"("id") ON DELETE CASCADE, + "created_at" timestamptz NOT NULL DEFAULT now(), + UNIQUE ("thread_id","user_id") +); +CREATE INDEX IF NOT EXISTS "idx_messagerie_participants_user_id" ON "public"."messagerie_participants" ("user_id"); + +-- ---------------------------------------------------------------------------- +-- 5.33 messagerie_messages +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."messagerie_messages" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "thread_id" uuid NOT NULL REFERENCES "public"."messagerie_threads"("id") ON DELETE CASCADE, + "sender_id" uuid NOT NULL REFERENCES "public"."utilisateurs"("id") ON DELETE CASCADE, + "contenu" text NOT NULL, + "created_at" timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_messagerie_messages_thread_id" ON "public"."messagerie_messages" ("thread_id"); +CREATE INDEX IF NOT EXISTS "idx_messagerie_messages_created_at" ON "public"."messagerie_messages" ("created_at" DESC); + +-- ---------------------------------------------------------------------------- +-- 5.34 banque_mots (Annonces sonores) +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."banque_mots" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "label" text, + "nom_fichier" text, + "storage_path" text, + "categorie" text DEFAULT 'general', + "format" text DEFAULT 'mp3', + "taille_bytes" bigint, + "description" text, + "duree_ms" integer, + "actif" boolean NOT NULL DEFAULT true, + "created_by" uuid, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_banque_mots_categorie" ON "public"."banque_mots" ("categorie"); +CREATE INDEX IF NOT EXISTS "idx_banque_mots_label" ON "public"."banque_mots" ("label"); +CREATE INDEX IF NOT EXISTS "idx_banque_mots_actif" ON "public"."banque_mots" ("actif") WHERE deleted_at IS NULL; + +-- ---------------------------------------------------------------------------- +-- 5.35 annonces_templates +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."annonces_templates" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "nom" text NOT NULL, + "type_annonce" text NOT NULL, + "description" text, + "sequence" jsonb NOT NULL DEFAULT '[]'::jsonb, + "variables" jsonb NOT NULL DEFAULT '[]'::jsonb, + "tts_texte" text, + "actif" boolean NOT NULL DEFAULT true, + "created_by" uuid, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_templates_type" ON "public"."annonces_templates" ("type_annonce"); +CREATE INDEX IF NOT EXISTS "idx_templates_actif" ON "public"."annonces_templates" ("actif") WHERE deleted_at IS NULL; + +-- ---------------------------------------------------------------------------- +-- 5.36 annonces_enregistrees +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."annonces_enregistrees" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "nom" text, + "type_annonce" text, + "template_id" uuid REFERENCES "public"."annonces_templates"("id") ON DELETE SET NULL, + "storage_path" text, + "nom_fichier" text, + "variables_values" jsonb DEFAULT '{}'::jsonb, + "mode_generation" text DEFAULT 'tts', + "duree_ms" integer, + "taille_bytes" bigint, + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "diffuse" boolean DEFAULT false, + "date_diffusion" timestamptz, + "notes" text, + "created_by" uuid, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz DEFAULT now(), + "deleted_at" timestamptz +); +CREATE INDEX IF NOT EXISTS "idx_annonces_enregistrees_template" ON "public"."annonces_enregistrees" ("template_id"); +CREATE INDEX IF NOT EXISTS "idx_annonces_enregistrees_gare" ON "public"."annonces_enregistrees" ("gare_id"); + +-- ---------------------------------------------------------------------------- +-- 5.37 annonces_diffusions +-- ---------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS "public"."annonces_diffusions" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY, + "annonce_id" uuid REFERENCES "public"."annonces_enregistrees"("id") ON DELETE SET NULL, + "gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL, + "audio_path" text, + "tts_texte" text, + "created_at" timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS "idx_annonces_diffusions_annonce" ON "public"."annonces_diffusions" ("annonce_id"); +CREATE INDEX IF NOT EXISTS "idx_annonces_diffusions_gare" ON "public"."annonces_diffusions" ("gare_id"); + +-- ============================================================================ +-- 6. TRIGGERS AUTOMATIQUES UPDATED_AT & AUDIT +-- ============================================================================ + +-- updated_at triggers +DO $$ +DECLARE + t text; + tables text[] := ARRAY[ + 'utilisateurs', 'gares', 'types_train', 'depots_technicentres', 'materiel-roulant', 'services_annuels', + 'lignes', 'lignes_gares', 'horaires', 'horaires_gares_desservies', 'horaires_substitutions', + 'perturbations', 'info_trafic', 'evenements', 'actualites', 'travaux', + 'infos_sillons', 'fiches_horaires', 'tarifs', 'parametres', 'menus_regionaux', + 'pages', 'page_personnalisation', 'info_gares', 'regles_quais', 'messagerie_threads', + 'profils_utilisateurs', 'banque_mots', 'annonces_templates', 'annonces_enregistrees' + ]; +BEGIN + FOREACH t IN ARRAY tables LOOP + EXECUTE format('DROP TRIGGER IF EXISTS "tr_%s_set_updated_at" ON "public".%I;', t, t); + EXECUTE format('CREATE TRIGGER "tr_%s_set_updated_at" BEFORE UPDATE ON "public".%I FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();', t, t); + END LOOP; +END $$; + +-- audit_logs triggers on sensitive tables +DO $$ +DECLARE + t text; + tables text[] := ARRAY[ + 'utilisateurs', 'gares', 'lignes', 'horaires', 'perturbations', + 'info_trafic', 'evenements', 'actualites', 'travaux', 'tarifs', 'parametres' + ]; +BEGIN + FOREACH t IN ARRAY tables LOOP + EXECUTE format('DROP TRIGGER IF EXISTS "tr_%s_audit" ON "public".%I;', t, t); + EXECUTE format('CREATE TRIGGER "tr_%s_audit" AFTER INSERT OR UPDATE OR DELETE ON "public".%I FOR EACH ROW EXECUTE FUNCTION "public"."log_audit"();', t, t); + END LOOP; +END $$; + +-- ============================================================================ +-- 7. SYNCHRONISATION NATIVE SUPABASE AUTH (auth.users <-> public.utilisateurs) +-- ============================================================================ +-- Cette fonction est déclenchée lors de la création ou modification d'un compte +-- dans Supabase Auth (via Dashboard Supabase Studio ou supabase.auth.signUp). +-- Elle met à jour automatiquement public.utilisateurs sans jamais bloquer GoTrue. + +CREATE OR REPLACE FUNCTION public.handle_new_auth_user() +RETURNS trigger +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public, extensions, auth, pg_temp +AS $$ +DECLARE + v_nom text; + v_prenom text; + v_role text; + v_region_id text; + v_region_nom text; + v_photo_url text; + v_actif boolean; + v_region_admin boolean; + v_permissions jsonb; +BEGIN + -- Extraction sécurisée des métadonnées Supabase Auth + v_nom := COALESCE( + NEW.raw_user_meta_data->>'nom', + NEW.raw_user_meta_data->>'last_name', + split_part(NEW.email, '@', 1), + 'Utilisateur' + ); + v_prenom := COALESCE( + NEW.raw_user_meta_data->>'prenom', + NEW.raw_user_meta_data->>'first_name', + '' + ); + v_role := COALESCE(NEW.raw_user_meta_data->>'role', 'utilisateur'); + v_region_id := NEW.raw_user_meta_data->>'region_id'; + v_region_nom := NEW.raw_user_meta_data->>'region_nom'; + v_photo_url := NEW.raw_user_meta_data->>'photo_url'; + v_actif := COALESCE((NEW.raw_user_meta_data->>'actif')::boolean, true); + v_region_admin := COALESCE((NEW.raw_user_meta_data->>'region_admin')::boolean, false); + v_permissions := COALESCE(NEW.raw_user_meta_data->'permissions', '{}'::jsonb); + + -- Insertion ou synchronisation dans public.utilisateurs + INSERT INTO public.utilisateurs ( + id, email, nom, prenom, role, actif, region_admin, + region_id, region_nom, permissions, photo_url, updated_at + ) VALUES ( + NEW.id, + lower(NEW.email), + v_nom, + v_prenom, + v_role, + v_actif, + v_region_admin, + v_region_id, + v_region_nom, + v_permissions, + v_photo_url, + NOW() + ) + ON CONFLICT (email) DO UPDATE SET + id = EXCLUDED.id, + nom = CASE WHEN EXCLUDED.nom <> '' AND EXCLUDED.nom <> 'Utilisateur' THEN EXCLUDED.nom ELSE public.utilisateurs.nom END, + prenom = CASE WHEN EXCLUDED.prenom <> '' THEN EXCLUDED.prenom ELSE public.utilisateurs.prenom END, + role = CASE WHEN public.utilisateurs.role = 'admin' THEN 'admin' ELSE EXCLUDED.role END, + actif = EXCLUDED.actif, + region_admin = COALESCE(public.utilisateurs.region_admin, EXCLUDED.region_admin), + region_id = COALESCE(EXCLUDED.region_id, public.utilisateurs.region_id), + region_nom = COALESCE(EXCLUDED.region_nom, public.utilisateurs.region_nom), + permissions = CASE WHEN public.utilisateurs.permissions <> '{}'::jsonb THEN public.utilisateurs.permissions ELSE EXCLUDED.permissions END, + photo_url = COALESCE(EXCLUDED.photo_url, public.utilisateurs.photo_url), + updated_at = NOW(); + + RETURN NEW; +EXCEPTION WHEN OTHERS THEN + -- Ne JAMAIS faire échouer l'insertion dans auth.users + RAISE WARNING 'handle_new_auth_user warning: %', SQLERRM; + RETURN NEW; +END; +$$; + +-- Attacher le trigger sur auth.users si le schéma auth existe +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'auth' AND table_name = 'users') THEN + DROP TRIGGER IF EXISTS "on_auth_user_created" ON auth.users; + CREATE TRIGGER "on_auth_user_created" + AFTER INSERT OR UPDATE ON auth.users + FOR EACH ROW EXECUTE FUNCTION public.handle_new_auth_user(); + END IF; +END $$; + +-- ============================================================================ +-- 8. FONCTIONS RPC MÉTIER +-- ============================================================================ + +-- Authentification personnalisée de secours (RPC authenticate_user) +CREATE OR REPLACE FUNCTION "public"."authenticate_user"(p_email text, p_password text) +RETURNS TABLE(id uuid, email text, nom text, prenom text, name text, role text, region_admin boolean, region_id text) +LANGUAGE plpgsql STABLE SECURITY DEFINER +SET search_path = public, extensions, auth, pg_temp +AS $$ +BEGIN + RETURN QUERY + SELECT + u.id::uuid, + u.email::text, + u.nom::text, + u.prenom::text, + (COALESCE(u.nom,'') || ' ' || COALESCE(u.prenom,''))::text as name, + u.role::text, + COALESCE(u.region_admin, false)::boolean as region_admin, + u.region_id::text + FROM public.utilisateurs u + WHERE lower(u.email) = lower(p_email) + AND u.password_hash IS NOT NULL + AND u.password_hash = crypt(p_password, u.password_hash) + AND COALESCE(u.actif, true) = true + AND u.deleted_at IS NULL; +END; +$$; + +-- Création utilisateur avec mot de passe hashé +CREATE OR REPLACE FUNCTION "public"."create_user_with_password"( + p_email text, p_nom text, p_prenom text, + p_role text DEFAULT 'utilisateur', p_actif boolean DEFAULT true, + p_password text DEFAULT NULL, p_photo_url text DEFAULT NULL, + p_region_admin boolean DEFAULT false, p_region_id text DEFAULT NULL +) +RETURNS TABLE(id uuid, email text, nom text, prenom text, role text, actif boolean, photo_url text, region_admin boolean, region_id text) +LANGUAGE plpgsql SECURITY DEFINER +SET search_path = public, extensions, auth, pg_temp +AS $$ +DECLARE + new_id uuid; +BEGIN + INSERT INTO public.utilisateurs ( + email, nom, prenom, role, actif, password_hash, photo_url, region_admin, region_id, created_at + ) VALUES ( + lower(p_email), p_nom, p_prenom, p_role, p_actif, + CASE WHEN p_password IS NOT NULL AND p_password != '' THEN crypt(p_password, gen_salt('bf')) ELSE NULL END, + p_photo_url, p_region_admin, p_region_id, NOW() + ) + RETURNING utilisateurs.id INTO new_id; + + RETURN QUERY SELECT + u.id, u.email::text, u.nom::text, u.prenom::text, u.role::text, + u.actif, u.photo_url::text, u.region_admin, u.region_id::text + FROM public.utilisateurs u WHERE u.id = new_id; +END; +$$; + +-- Mise à jour mot de passe +CREATE OR REPLACE FUNCTION "public"."update_user_password"(p_user_id uuid, p_password text) +RETURNS void +LANGUAGE plpgsql SECURITY DEFINER +SET search_path = public, extensions, auth, pg_temp +AS $$ +BEGIN + IF p_password IS NULL OR p_password = '' THEN + RAISE EXCEPTION 'Le mot de passe ne peut pas être vide'; + END IF; + + UPDATE public.utilisateurs + SET password_hash = crypt(p_password, gen_salt('bf')), + updated_at = NOW() + WHERE id = p_user_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Utilisateur non trouvé'; + END IF; +END; +$$; + +-- Détails agrégés d'une gare (quais + transports) +CREATE OR REPLACE FUNCTION "public"."get_gare_details"(p_gare_id uuid) +RETURNS TABLE(id uuid, nom text, type text, services "public"."service_type"[], quais jsonb, transports jsonb, created_at timestamptz, updated_at timestamptz) +LANGUAGE plpgsql STABLE SECURITY DEFINER +SET search_path = public, extensions, auth, pg_temp +AS $$ +BEGIN + RETURN QUERY SELECT + g.id, + g.nom, + g.type::text, + g.services, + COALESCE(( + SELECT jsonb_agg(jsonb_build_object('id', q.id, 'nom', q.nom, 'distance_metres', q.distance_metres)) + FROM public.gare_quais q WHERE q.gare_id = g.id + ), '[]'::jsonb) as quais, + COALESCE(( + SELECT jsonb_agg(jsonb_build_object('id', t.id, 'type', t.type, 'ligne', t.ligne)) + FROM public.gare_transports t WHERE t.gare_id = g.id + ), '[]'::jsonb) as transports, + g.created_at, + g.updated_at + FROM public.gares g + WHERE g.id = p_gare_id AND g.deleted_at IS NULL; +END; +$$; + +-- ============================================================================ +-- 9. ROW LEVEL SECURITY (RLS) & POLICIES +-- ============================================================================ +-- Activer le RLS sur l'ensemble des 37 tables du schéma public +DO $$ +DECLARE + t text; + tables text[] := ARRAY[ + 'audit_logs', 'utilisateurs', 'profils_utilisateurs', 'gares', 'gare_quais', + 'gare_transports', 'types_train', 'depots_technicentres', 'materiel-roulant', 'services_annuels', + 'lignes', 'lignes_gares', 'horaires', 'horaires_gares_desservies', 'horaires_substitutions', + 'perturbations', 'perturbations_modifications_parcours', 'info_trafic', 'evenements', + 'actualites', 'travaux', 'travaux_substitutions', 'infos_sillons', 'fiches_horaires', + 'tarifs', 'parametres', 'menus_regionaux', 'pages', 'page_personnalisation', + 'info_gares', 'regles_quais', 'messagerie_threads', 'messagerie_participants', + 'messagerie_messages', 'banque_mots', 'annonces_templates', 'annonces_enregistrees', + 'annonces_diffusions' + ]; +BEGIN + FOREACH t IN ARRAY tables LOOP + EXECUTE format('ALTER TABLE "public".%I ENABLE ROW LEVEL SECURITY;', t); + EXECUTE format('DROP POLICY IF EXISTS "policy_all_%s" ON "public".%I;', t, t); + EXECUTE format('CREATE POLICY "policy_all_%s" ON "public".%I FOR ALL TO anon, authenticated, service_role USING (true) WITH CHECK (true);', t, t); + END LOOP; +END $$; + +-- Politiques spécifiques pour l'app mobile (profils_utilisateurs) +DROP POLICY IF EXISTS "Lecture profil propre" ON "public"."profils_utilisateurs"; +DROP POLICY IF EXISTS "Insertion profil propre" ON "public"."profils_utilisateurs"; +DROP POLICY IF EXISTS "Mise à jour profil propre" ON "public"."profils_utilisateurs"; +DROP POLICY IF EXISTS "Suppression profil propre" ON "public"."profils_utilisateurs"; + +CREATE POLICY "Lecture profil propre" ON "public"."profils_utilisateurs" FOR SELECT USING (true); +CREATE POLICY "Insertion profil propre" ON "public"."profils_utilisateurs" FOR INSERT WITH CHECK (true); +CREATE POLICY "Mise à jour profil propre" ON "public"."profils_utilisateurs" FOR UPDATE USING (true) WITH CHECK (true); +CREATE POLICY "Suppression profil propre" ON "public"."profils_utilisateurs" FOR DELETE USING (true); + +-- ============================================================================ +-- 10. SUPABASE STORAGE BUCKETS & POLICIES +-- ============================================================================ +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'storage') THEN + + -- 1. Buckets fiches horaires + INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types") + VALUES ('fiches-horaires', 'fiches-horaires', true, 52428800, ARRAY['application/pdf']) + ON CONFLICT ("id") DO UPDATE SET "public" = true; + + INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types") + VALUES ('Fiches_Horaires', 'Fiches_Horaires', true, 52428800, ARRAY['application/pdf']) + ON CONFLICT ("id") DO UPDATE SET "public" = true; + + -- 2. Bucket matériel roulant (photos de trains) + INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types") + VALUES ('materiel-roulant', 'materiel-roulant', true, 10485760, ARRAY['image/jpeg','image/png','image/webp','image/gif','image/svg+xml']) + ON CONFLICT ("id") DO UPDATE SET "public" = true; + + -- 3. Bucket annonces sonores (jingles et fichiers audio) + INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types") + VALUES ('banque-annonces', 'banque-annonces', true, 20971520, ARRAY['audio/mpeg','audio/wav','audio/mp3','audio/ogg']) + ON CONFLICT ("id") DO UPDATE SET "public" = true; + + -- Politiques Storage pour l'accès aux objets + DROP POLICY IF EXISTS "Public access on storage fiches-horaires" ON storage.objects; + DROP POLICY IF EXISTS "Public access on storage materiel-roulant" ON storage.objects; + DROP POLICY IF EXISTS "Public access on storage banque-annonces" ON storage.objects; + + CREATE POLICY "Public access on storage fiches-horaires" + ON storage.objects FOR ALL TO anon, authenticated, service_role + USING (bucket_id IN ('fiches-horaires', 'Fiches_Horaires')) + WITH CHECK (bucket_id IN ('fiches-horaires', 'Fiches_Horaires')); + + CREATE POLICY "Public access on storage materiel-roulant" + ON storage.objects FOR ALL TO anon, authenticated, service_role + USING (bucket_id = 'materiel-roulant') + WITH CHECK (bucket_id = 'materiel-roulant'); + + CREATE POLICY "Public access on storage banque-annonces" + ON storage.objects FOR ALL TO anon, authenticated, service_role + USING (bucket_id = 'banque-annonces') + WITH CHECK (bucket_id = 'banque-annonces'); + + END IF; +EXCEPTION WHEN OTHERS THEN + RAISE NOTICE 'Storage buckets notice: %', SQLERRM; +END $$; + +-- ============================================================================ +-- 11. PRIVILÈGES & GRANTS (anon, authenticated, service_role) +-- ============================================================================ +GRANT USAGE ON SCHEMA "public" TO "postgres", "anon", "authenticated", "service_role"; + +-- Accès complet sur toutes les tables +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA "public" TO "anon", "authenticated", "service_role"; + +-- Séquences +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "public" TO "anon", "authenticated", "service_role"; + +-- Fonctions +GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "public" TO "anon", "authenticated", "service_role"; + +-- Privilèges par défaut pour les futures créations +ALTER DEFAULT PRIVILEGES IN SCHEMA "public" GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "anon", "authenticated", "service_role"; +ALTER DEFAULT PRIVILEGES IN SCHEMA "public" GRANT USAGE, SELECT ON SEQUENCES TO "anon", "authenticated", "service_role"; +ALTER DEFAULT PRIVILEGES IN SCHEMA "public" GRANT EXECUTE ON FUNCTIONS TO "anon", "authenticated", "service_role"; + +-- Schéma extensions +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'extensions') THEN + GRANT USAGE ON SCHEMA "extensions" TO "postgres", "anon", "authenticated", "service_role"; + GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "extensions" TO "postgres", "anon", "authenticated", "service_role"; + END IF; +END $$; + +-- ============================================================================ +-- 12. DONNÉES INITIALES (SEEDING) +-- ============================================================================ + +-- 12.1 Paramètres système par défaut +INSERT INTO "public"."parametres" (cle, valeur) VALUES + ('general', '{"nom_site": "Ferrovia Panel", "description_site": "Système de supervision et gestion ferroviaire", "region": "Nationale", "logo_header_url": ""}'::jsonb), + ('tarifs', '{"billets": [], "abonnements": [], "remises": []}'::jsonb), + ('theme', '{"primary_color": "#0088CE", "dark_mode": false}'::jsonb) +ON CONFLICT (cle) DO NOTHING; + +-- 12.2 Types de train par défaut +INSERT INTO "public"."types_train" (id, nom, code, description, couleur, color, type_train, actif) VALUES + (1, 'TER', 'TER', 'Transport Express Régional', '#0088CE', '#0088CE', 'TER', true), + (2, 'TGV INOUI', 'TGV', 'Train à Grande Vitesse INOUI', '#7A9A01', '#7A9A01', 'TGV', true), + (3, 'OUIGO', 'OUIGO', 'Offre grande vitesse low-cost', '#009AA6', '#009AA6', 'OUIGO', true), + (4, 'Intercités', 'IC', 'Lignes classiques nationales et de nuit', '#CD0037', '#CD0037', 'Intercités', true), + (5, 'Transilien', 'TRA', 'Réseau de banlieue Île-de-France', '#0055A5', '#0055A5', 'Transilien', true), + (6, 'Fret', 'FRET', 'Transport ferroviaire de marchandises', '#64748B', '#64748B', 'Fret', true) +ON CONFLICT (id) DO UPDATE SET + nom = EXCLUDED.nom, + code = EXCLUDED.code, + couleur = EXCLUDED.couleur, + color = EXCLUDED.color, + actif = EXCLUDED.actif; + +-- 12.3 Dépôts et Technicentres de maintenance réseau par défaut +INSERT INTO "public"."depots_technicentres" ("nom", "code", "ville", "gare_proche", "capacite_max", "specialites") VALUES + ('Technicentre Paris-Nord (La Chapelle)', 'TPN-LCP', 'Paris', 'Paris Gare du Nord', 24, '["TGV", "TER", "Eurostar", "Maintenance Lourde"]'::jsonb), + ('Technicentre Atlantique (Châtillon)', 'TPA-CHT', 'Paris', 'Paris Montparnasse', 30, '["TGV InOui", "OUIGO", "Grandes Lignes"]'::jsonb), + ('Technicentre Est Européen (Pantin)', 'TEE-PAN', 'Paris', 'Paris Gare de l''Est', 18, '["TGV", "ICE", "Révision mi-vie"]'::jsonb), + ('Technicentre Paris-Sud-Est (Villeneuve)', 'TPSE-VLL', 'Paris', 'Paris Gare de Lyon', 28, '["TGV Sud-Est", "Automotrices", "Climatisation"]'::jsonb), + ('Technicentre Bretagne (Rennes)', 'TB-REN', 'Rennes', 'Rennes', 16, '["TER BreizhGo", "Régiolis", "Z2N", "Bogies"]'::jsonb), + ('Dépôt Pays de la Loire (Nantes-Blottereau)', 'DPL-NTS', 'Nantes', 'Nantes', 14, '["TER Aléop", "ZTER", "AGC Diesel/Electrique"]'::jsonb), + ('Technicentre Auvergne-Rhône-Alpes (Gerland)', 'TARA-GER', 'Lyon', 'Lyon Part-Dieu', 22, '["TER AURA", "TGV", "Maintenance Préventive"]'::jsonb), + ('Dépôt Marseille-Blancarde', 'DMB-MRS', 'Marseille', 'Marseille Saint-Charles', 15, '["TER Zou", "Rames Réversibles", "Electrique"]'::jsonb), + ('Technicentre Nouvelle-Aquitaine (Bordeaux)', 'TNA-BDX', 'Bordeaux', 'Bordeaux Saint-Jean', 16, '["TER Nouvelle-Aquitaine", "Coradia Polyvalent"]'::jsonb), + ('Technicentre Nord-Pas-de-Calais (Hellemmes)', 'TNPDC-HLM', 'Lille', 'Lille Flandres', 20, '["TER Hauts-de-France", "Grand Carénage"]'::jsonb), + ('Dépôt Grand Est (Strasbourg-Cronenbourg)', 'DGE-STB', 'Strasbourg', 'Strasbourg', 14, '["TER Fluo", "Régiolis Transfrontalier"]'::jsonb) +ON CONFLICT ("code") DO UPDATE SET + nom = EXCLUDED.nom, + ville = EXCLUDED.ville, + gare_proche = EXCLUDED.gare_proche, + capacite_max = EXCLUDED.capacite_max, + specialites = EXCLUDED.specialites; + +-- 12.4 Création des comptes Administrateur dans Supabase Auth (auth.users) +-- Mot de passe par défaut : password (crypté en bcrypt bf $2a$10$) +DO $$ +DECLARE + v_admin1_id uuid := 'a0000000-0000-0000-0000-000000000001'::uuid; + v_admin2_id uuid := 'a0000000-0000-0000-0000-000000000002'::uuid; + v_pw_hash text; +BEGIN + -- Hash bcrypt compatible Supabase Auth / GoTrue + v_pw_hash := crypt('password', gen_salt('bf', 10)); + + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'auth' AND table_name = 'users') THEN + + -- Administrateur 1 : admin@ferrovia.fr + IF NOT EXISTS (SELECT 1 FROM auth.users WHERE email = 'admin@ferrovia.fr') THEN + INSERT INTO auth.users ( + instance_id, id, aud, role, email, encrypted_password, + email_confirmed_at, recovery_sent_at, last_sign_in_at, + raw_app_meta_data, raw_user_meta_data, created_at, updated_at, + confirmation_token, email_change, email_change_token_new, recovery_token + ) VALUES ( + '00000000-0000-0000-0000-000000000000', + v_admin1_id, + 'authenticated', + 'authenticated', + 'admin@ferrovia.fr', + v_pw_hash, + NOW(), NOW(), NOW(), + '{"provider":"email","providers":["email"]}'::jsonb, + '{"nom":"Admin","prenom":"Ferrovia","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb, + NOW(), NOW(), + '', '', '', '' + ); + + BEGIN + INSERT INTO auth.identities ( + id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at + ) VALUES ( + v_admin1_id::text, + v_admin1_id, + jsonb_build_object('sub', v_admin1_id::text, 'email', 'admin@ferrovia.fr'), + 'email', + NOW(), NOW(), NOW() + ); + EXCEPTION WHEN OTHERS THEN NULL; + END; + ELSE + UPDATE auth.users + SET encrypted_password = v_pw_hash, + email_confirmed_at = COALESCE(email_confirmed_at, NOW()), + raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || '{"nom":"Admin","prenom":"Ferrovia","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb + WHERE email = 'admin@ferrovia.fr'; + END IF; + + -- Administrateur 2 : admin@mrpatator.fr + IF NOT EXISTS (SELECT 1 FROM auth.users WHERE email = 'admin@mrpatator.fr') THEN + INSERT INTO auth.users ( + instance_id, id, aud, role, email, encrypted_password, + email_confirmed_at, recovery_sent_at, last_sign_in_at, + raw_app_meta_data, raw_user_meta_data, created_at, updated_at, + confirmation_token, email_change, email_change_token_new, recovery_token + ) VALUES ( + '00000000-0000-0000-0000-000000000000', + v_admin2_id, + 'authenticated', + 'authenticated', + 'admin@mrpatator.fr', + v_pw_hash, + NOW(), NOW(), NOW(), + '{"provider":"email","providers":["email"]}'::jsonb, + '{"nom":"Admin","prenom":"MrPatator","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb, + NOW(), NOW(), + '', '', '', '' + ); + + BEGIN + INSERT INTO auth.identities ( + id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at + ) VALUES ( + v_admin2_id::text, + v_admin2_id, + jsonb_build_object('sub', v_admin2_id::text, 'email', 'admin@mrpatator.fr'), + 'email', + NOW(), NOW(), NOW() + ); + EXCEPTION WHEN OTHERS THEN NULL; + END; + ELSE + UPDATE auth.users + SET encrypted_password = v_pw_hash, + email_confirmed_at = COALESCE(email_confirmed_at, NOW()), + raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || '{"nom":"Admin","prenom":"MrPatator","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb + WHERE email = 'admin@mrpatator.fr'; + END IF; + + END IF; +EXCEPTION WHEN OTHERS THEN + RAISE NOTICE 'Auth seeding notice: %', SQLERRM; +END $$; + +-- 12.4 Insertion / Synchronisation dans public.utilisateurs +INSERT INTO "public"."utilisateurs" ( + id, email, nom, prenom, role, actif, region_admin, password_hash, permissions +) VALUES ( + 'a0000000-0000-0000-0000-000000000001'::uuid, + 'admin@ferrovia.fr', + 'Admin', + 'Ferrovia', + 'admin', + true, + true, + crypt('password', gen_salt('bf', 10)), + '{"admin": true, "super_admin": true, "all_regions": true}'::jsonb +) ON CONFLICT (email) DO UPDATE SET + nom = EXCLUDED.nom, + prenom = EXCLUDED.prenom, + role = 'admin', + actif = true, + region_admin = true, + password_hash = EXCLUDED.password_hash, + permissions = EXCLUDED.permissions, + updated_at = NOW(); + +INSERT INTO "public"."utilisateurs" ( + id, email, nom, prenom, role, actif, region_admin, password_hash, permissions +) VALUES ( + 'a0000000-0000-0000-0000-000000000002'::uuid, + 'admin@mrpatator.fr', + 'Admin', + 'MrPatator', + 'admin', + true, + true, + crypt('password', gen_salt('bf', 10)), + '{"admin": true, "super_admin": true, "all_regions": true}'::jsonb +) ON CONFLICT (email) DO UPDATE SET + nom = EXCLUDED.nom, + prenom = EXCLUDED.prenom, + role = 'admin', + actif = true, + region_admin = true, + password_hash = EXCLUDED.password_hash, + permissions = EXCLUDED.permissions, + updated_at = NOW(); + +-- ============================================================================ +-- FIN DU FICHIER SCHEME.sql +-- Base Ferrovia prête : 37 Tables, 3 ENUMs, Triggers Supabase Auth, +-- 4 Buckets Storage, RLS & Rôles anon/authenticated configurés. +-- ============================================================================ diff --git a/package-lock.json b/package-lock.json index 027c99a..b562806 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@sncf/bootstrap-sncf.communication": "^4.3.1-r5", + "@sncf/bootstrap-sncf.metier": "^4.3.1-r5", "@supabase/supabase-js": "^2.112.4", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -3208,735 +3209,25 @@ "yarn": "^1.7.0" } }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", - "license": "MIT", - "peer": true - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "license": "MIT", - "peer": true - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "license": "MIT", - "peer": true - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", - "license": "MIT", - "peer": true - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "license": "MIT", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "license": "MIT", - "peer": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/enhanced-resolve": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", - "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", - "peer": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/enhanced-resolve/node_modules/memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "license": "MIT", - "peer": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "license": "MIT", - "peer": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "license": "MIT", - "peer": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/@sncf/bootstrap-sncf.metier": { + "version": "4.3.1-r5", + "resolved": "https://registry.npmjs.org/@sncf/bootstrap-sncf.metier/-/bootstrap-sncf.metier-4.3.1-r5.tgz", + "integrity": "sha512-2TjKXokg0ssXtodqe7tdOQE7HncNGfhuhVSTi+c7zjmP1WuE5GebVaifS3KQ7oSKvkN4eiyzOJXxPUH/X+nWgQ==", "license": "MIT", "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "license": "MIT", - "peer": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "license": "MIT", - "peer": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "license": "MIT", - "peer": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/null-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-1.0.0.tgz", - "integrity": "sha512-mYLDjDVTkjTlFoidxRhzO75rdcwfVXfw5G5zpj8sXnBkHtKJxMk4hTcRR4i5SOhDB6EvcQuYriy6IV23eq6uog==", - "license": "MIT", - "dependencies": { - "loader-utils": "^1.2.3", - "schema-utils": "^1.0.0" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "webpack": "^4.3.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "license": "MIT", - "peer": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT", - "peer": true - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "license": "MIT", - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/terser-webpack-plugin": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.6.tgz", - "integrity": "sha512-2lBVf/VMVIddjSn3GqbT90GvIJ/eYXJkt8cTzU7NbjKqK8fwv18Ftr4PlbF46b/e88743iZFL5Dtr/rC4hjIeA==", - "license": "MIT", - "peer": true, - "dependencies": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "webpack": "^4.0.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "optionalDependencies": { - "chokidar": "^3.4.1", - "watchpack-chokidar2": "^2.0.1" - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/webpack": { - "version": "4.47.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.47.0.tgz", - "integrity": "sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.5.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - }, - "webpack-command": { - "optional": true - } - } - }, - "node_modules/@sncf/bootstrap-sncf.communication/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" + "bootstrap": "4.3.1", + "chart.js": "^2.7.2", + "chartjs-plugin-zoom": "^0.6.3", + "flatpickr": "^4.6.2", + "hugo-bin": "^0.44.1", + "ion-rangeslider": "^2.2.0", + "jquery": "^3.3.1", + "lodash": "^4.17.11", + "null-loader": "^1.0.0", + "popper.js": "^1.14.1", + "swiper": "^4.2.2", + "whatwg-fetch": "^3.0.0", + "yarn": "^1.7.0" } }, "node_modules/@supabase/auth-js": { @@ -4493,15 +3784,6 @@ "node": ">= 6" } }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -4597,16 +3879,6 @@ "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -5117,44 +4389,6 @@ "license": "ISC" }, "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-code-frame": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", - "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "node_modules/@webassemblyjs/helper-code-frame/node_modules/@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", @@ -5166,23 +4400,35 @@ "@webassemblyjs/wast-parser": "1.9.0" } }, - "node_modules/@webassemblyjs/helper-code-frame/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", "license": "MIT", "peer": true }, - "node_modules/@webassemblyjs/helper-code-frame/node_modules/@webassemblyjs/wast-printer": { + "node_modules/@webassemblyjs/helper-api-error": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" + "@webassemblyjs/wast-printer": "1.9.0" } }, "node_modules/@webassemblyjs/helper-fsm": { @@ -5202,25 +4448,6 @@ "@webassemblyjs/ast": "1.9.0" } }, - "node_modules/@webassemblyjs/helper-module-context/node_modules/@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" - } - }, - "node_modules/@webassemblyjs/helper-module-context/node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "license": "MIT", - "peer": true - }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", @@ -5232,101 +4459,122 @@ "@xtuc/long": "4.2.2" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "license": "MIT" }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", "license": "MIT", + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "license": "Apache-2.0", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "license": "MIT", + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, "node_modules/@webassemblyjs/wast-parser": { @@ -5344,46 +4592,15 @@ "@xtuc/long": "4.2.2" } }, - "node_modules/@webassemblyjs/wast-parser/node_modules/@webassemblyjs/ast": { + "node_modules/@webassemblyjs/wast-printer": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" - } - }, - "node_modules/@webassemblyjs/wast-parser/node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", - "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/wast-parser/node_modules/@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/wast-parser/node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", "@xtuc/long": "4.2.2" } }, @@ -5462,18 +4679,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -5561,9 +4766,9 @@ } }, "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -5578,9 +4783,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -6022,9 +5227,9 @@ } }, "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "license": "MIT", "peer": true }, @@ -6489,6 +5694,20 @@ "node": ">=0.10.0" } }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz", + "integrity": "sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.2", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -6954,9 +6173,9 @@ "license": "MIT" }, "node_modules/bn.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", - "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.5.tgz", + "integrity": "sha512-Vq886eXykuP5E6HcKSSStP3bJgrE6In5WKxVUvJ8XGpWWYs2xZHWqUwzCtGgEtBcxyd57KBFDPFoUfNzdaHCNg==", "license": "MIT", "peer": true }, @@ -7132,13 +6351,13 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz", - "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.6.tgz", + "integrity": "sha512-sd+Q65fjlWCYWtZKXiKfrUc8d+4jtp/8f0W2NkwzLtoW4bI6UDnWusLWIurHnmurW0XShIRxpwiOX4EoPtXUAg==", "license": "ISC", "peer": true, "dependencies": { - "bn.js": "^5.2.2", + "bn.js": "^5.2.3", "browserify-rsa": "^4.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", @@ -7873,33 +7092,6 @@ "node": ">=0.10.0" } }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -8084,9 +7276,9 @@ "license": "MIT" }, "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.10.0.tgz", + "integrity": "sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==", "license": "MIT" }, "node_modules/colorette": { @@ -8108,12 +7300,12 @@ } }, "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "license": "MIT", "engines": { - "node": ">= 12" + "node": ">= 10" } }, "node_modules/common-tags": { @@ -8449,9 +7641,9 @@ } }, "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "license": "MIT", "peer": true }, @@ -8592,88 +7784,6 @@ "utrie": "^1.0.2" } }, - "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "license": "MIT", - "dependencies": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/css-prefers-color-scheme": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", @@ -9293,14 +8403,13 @@ } }, "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "license": "MIT", "peer": true, "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "^0.1.0" }, "engines": { "node": ">=0.10.0" @@ -9429,9 +8538,9 @@ } }, "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "license": "MIT", "peer": true }, @@ -9821,9 +8930,9 @@ } }, "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "license": "MIT", "peer": true }, @@ -9873,16 +8982,81 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", - "license": "MIT", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "peer": true, "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=6.9.0" + } + }, + "node_modules/enhanced-resolve/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT", + "peer": true + }, + "node_modules/enhanced-resolve/node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "license": "MIT", + "peer": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/enhanced-resolve/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/enhanced-resolve/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT", + "peer": true + }, + "node_modules/enhanced-resolve/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/enhanced-resolve/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" } }, "node_modules/entities": { @@ -10045,9 +9219,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "license": "MIT" }, "node_modules/es-object-atoms": { @@ -10554,59 +9728,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", - "license": "MIT", - "dependencies": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/eslint/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -10871,56 +9992,6 @@ "ms": "2.0.0" } }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/expand-brackets/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -11030,14 +10101,13 @@ } }, "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "license": "MIT", "peer": true, "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "is-extendable": "^0.1.0" }, "engines": { "node": ">=0.10.0" @@ -11076,27 +10146,18 @@ "node": ">=0.10.0" } }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz", + "integrity": "sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==", "license": "MIT", "peer": true, "dependencies": { - "is-extendable": "^0.1.0" + "is-accessor-descriptor": "^1.0.2", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "node_modules/fast-deep-equal": { @@ -12086,12 +11147,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -12477,9 +11532,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -12621,36 +11676,13 @@ "node": ">=12" } }, - "node_modules/html-webpack-plugin": { - "version": "5.6.6", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz", - "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==", + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } + "node": ">= 12" } }, "node_modules/html2canvas": { @@ -13087,16 +12119,16 @@ } }, "node_modules/is-accessor-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", - "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.2.tgz", + "integrity": "sha512-AIbwAcazqP3R65dGvqk1V+a+vE5Fg1yu/ZKMOiBWSUIXXiwQkYmXQcVa2O0nh0tSDKDFKxG2mY7dB1Sr4hEP1g==", "license": "MIT", "peer": true, "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.3" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.4" } }, "node_modules/is-array-buffer": { @@ -13265,9 +12297,9 @@ } }, "node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.8.tgz", + "integrity": "sha512-SceYGWXvdqlWa/OnQ5FQuV+NxvNmMRhMw/w9AHkH71hTzveND4BTYgvp16g+oITK47qbOl/3D0bl0iygehWAWQ==", "license": "MIT", "peer": true, "dependencies": { @@ -13294,14 +12326,11 @@ } }, "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "license": "MIT", "peer": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, "engines": { "node": ">=0.10.0" } @@ -15208,16 +14237,13 @@ } }, "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", "license": "MIT", + "peer": true, "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=4.3.0 <5.0.0 || >=5.10" } }, "node_modules/loader-utils": { @@ -15561,9 +14587,9 @@ } }, "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "license": "MIT", "peer": true }, @@ -15627,26 +14653,6 @@ "node": ">=4" } }, - "node_modules/mini-css-extract-plugin": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.0.tgz", - "integrity": "sha512-540P2c5dYnJlyJxTaSloliZexv8rji6rY8FhQN+WF/82iHQfA23j/xtJx97L+mXOML27EqksSek/g4eK7jaL3g==", - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -15717,6 +14723,19 @@ "node": ">=0.10.0" } }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -15799,9 +14818,9 @@ } }, "node_modules/nan": { - "version": "2.25.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.25.0.tgz", - "integrity": "sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.29.0.tgz", + "integrity": "sha512-GlGk3HIvitbvs+LT3g6XUP1kpirKNvmDFwF/bmo6XNWSb/eYEs/O4bfgIEIXCZ+lIOTS5xNwDvSGMw6FJdAhtA==", "license": "MIT", "optional": true, "peer": true @@ -15847,6 +14866,61 @@ "node": ">=0.10.0" } }, + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/is-descriptor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz", + "integrity": "sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.2", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -16069,6 +15143,62 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/null-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-1.0.0.tgz", + "integrity": "sha512-mYLDjDVTkjTlFoidxRhzO75rdcwfVXfw5G5zpj8sXnBkHtKJxMk4hTcRR4i5SOhDB6EvcQuYriy6IV23eq6uog==", + "license": "MIT", + "dependencies": { + "loader-utils": "^1.2.3", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.3.0" + } + }, + "node_modules/null-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/null-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/null-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, "node_modules/nwsapi": { "version": "2.2.23", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", @@ -16099,33 +15229,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/object-copy/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -16683,6 +15786,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT", "optional": true, "peer": true @@ -16736,9 +15840,9 @@ } }, "node_modules/pbkdf2": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", - "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.7.tgz", + "integrity": "sha512-nS4mvFgVwUrecPTRdpdseM2fwpdfnX0/HibA8DgBRQtihUAzHQYJFU2l+OlEtiXtpqkrMvCPDwyXMUnASDt6Qw==", "license": "MIT", "peer": true, "dependencies": { @@ -16747,7 +15851,7 @@ "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", "sha.js": "^2.4.12", - "to-buffer": "^1.2.1" + "to-buffer": "^1.2.2" }, "engines": { "node": ">= 0.10" @@ -17473,28 +16577,6 @@ "postcss": "^8.2" } }, - "node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "license": "MIT", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, "node_modules/postcss-logical": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", @@ -17647,9 +16729,9 @@ } }, "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz", + "integrity": "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -17675,9 +16757,9 @@ } }, "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz", + "integrity": "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -18158,15 +17240,6 @@ "postcss": "^8.2.15" } }, - "node_modules/postcss-svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, "node_modules/postcss-svgo/node_modules/css-tree": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", @@ -18186,6 +17259,15 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "license": "CC0-1.0" }, + "node_modules/postcss-svgo/node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/postcss-svgo/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -18196,17 +17278,17 @@ } }, "node_modules/postcss-svgo/node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.4.tgz", + "integrity": "sha512-2GJ4h3rl13qYTdwllaK6QlL8tG+UrM8626V2Ylcd/yBUv2Y/EwLsYisVV6UDCRmlk+C74G8nMpxJCk0RWPdDCw==", "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", + "sax": "^1.5.0", "stable": "^0.1.8" }, "bin": { @@ -18434,9 +17516,9 @@ } }, "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "license": "MIT", "peer": true }, @@ -18859,6 +17941,634 @@ } } }, + "node_modules/react-scripts/node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/react-scripts/node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/react-scripts/node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "license": "MIT", + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/react-scripts/node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "license": "MIT", + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/react-scripts/node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/html-webpack-plugin": { + "version": "5.6.8", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.8.tgz", + "integrity": "sha512-MZmKQcTnhEh1SPSyMiEytIeDZDUoBZVorNHivQGXMASHf/BSGGOrKa2xQ5bGx3TCe1n109ecCt+cpww7wwWhKA==", + "license": "MIT", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x || 2.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/react-scripts/node_modules/mini-css-extract-plugin": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", + "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/react-scripts/node_modules/minimizer-webpack-plugin": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.13.0.tgz", + "integrity": "sha512-OO1NF2zHj6sf4CNv2AsUWYcS8WEwkVKQDXxxKzlOxx1Xl+WlqYtVC+oNlGNwGkLAOnpj277BSHypWwRtd9w4JQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.3", + "terser": "^5.51.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@napi-rs/image": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "imagemin": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "sharp": { + "optional": true + }, + "svgo": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/react-scripts/node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "license": "MIT", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-scripts/node_modules/source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/react-scripts/node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/react-scripts/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/react-scripts/node_modules/webpack": { + "version": "5.111.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.111.1.tgz", + "integrity": "sha512-cNypaz0RP+S4cvQVi1M/3bRUkvNP0xZpL0TjFx+c6jg64VbxD+2weWDgY9UnjRI6dhZgtaj8DU76GGFcJ79xPQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.25.0", + "es-module-lexer": "^2.1.0", + "events": "^3.2.0", + "graceful-fs": "^4.2.11", + "mime-db": "^1.54.0", + "minimizer-webpack-plugin": "^5.7.0", + "schema-utils": "^4.5.0", + "tapable": "^2.3.0", + "watchpack": "^2.5.2", + "webpack-sources": "^3.5.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/webpack-sources": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.6.0.tgz", + "integrity": "sha512-EIMmPVvNI0CYXyRPp34F2Qk7W37/BZVZIofS6LAUnrNjCVahS+hO4mxwFJDjQjODTGZDtVzLYoN2+1dYkhk9qA==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -18979,6 +18689,33 @@ "node": ">=0.10.0" } }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/regex-parser": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", @@ -19560,44 +19297,6 @@ "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", "license": "CC0-1.0" }, - "node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", - "license": "MIT", - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - } - } - }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -19623,14 +19322,14 @@ "license": "MIT" }, "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.5.0.tgz", + "integrity": "sha512-zJlMCZ0cAR5p/Y4oVpRoqioDMJcGxaXRrQ/4rP4WyR84vc5z/DolXdbvXeDpTwbtocDFr2rhPqHPErDCUtz2kA==", "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", + "@types/json-schema": "^7.0.15", + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1", "ajv-keywords": "^5.1.0" }, "engines": { @@ -19642,9 +19341,9 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -19951,29 +19650,6 @@ "node": ">=0.10.0" } }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -20392,6 +20068,20 @@ "node": ">=0.10.0" } }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz", + "integrity": "sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.2", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", @@ -20428,56 +20118,6 @@ "ms": "2.0.0" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/snapdragon/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -20563,27 +20203,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", - "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", - "license": "MIT", - "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -20676,6 +20295,33 @@ "node": ">=0.10.0" } }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -20868,33 +20514,6 @@ "node": ">=0.10.0" } }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "license": "MIT", - "peer": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -21320,22 +20939,6 @@ "node": ">=0.8.0" } }, - "node_modules/style-loader": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", - "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -21730,9 +21333,9 @@ } }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "license": "MIT", "engines": { "node": ">=6" @@ -21852,9 +21455,9 @@ } }, "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "version": "5.51.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.51.2.tgz", + "integrity": "sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -21870,37 +21473,209 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", - "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.6.tgz", + "integrity": "sha512-2lBVf/VMVIddjSn3GqbT90GvIJ/eYXJkt8cTzU7NbjKqK8fwv18Ftr4PlbF46b/e88743iZFL5Dtr/rC4hjIeA==", "license": "MIT", + "peer": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">= 6.9.0" }, "peerDependencies": { - "webpack": "^5.1.0" + "webpack": "^4.0.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT", + "peer": true + }, + "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "license": "MIT", + "peer": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/terser/node_modules/commander": { @@ -22186,6 +21961,61 @@ "node": ">=8.0" } }, + "node_modules/to-regex/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/is-descriptor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz", + "integrity": "sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.2", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -22589,16 +22419,6 @@ "node": ">=0.10.0" } }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -22973,16 +22793,18 @@ } }, "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "license": "MIT", + "peer": true, "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" }, - "engines": { - "node": ">=10.13.0" + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" } }, "node_modules/watchpack-chokidar2": { @@ -23056,20 +22878,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/watchpack-chokidar2/node_modules/chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -23094,6 +22902,21 @@ "fsevents": "^1.2.7" } }, + "node_modules/watchpack-chokidar2/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/watchpack-chokidar2/node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -23111,20 +22934,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/watchpack-chokidar2/node_modules/fsevents": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", @@ -23185,13 +22994,31 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/watchpack-chokidar2/node_modules/is-descriptor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz", + "integrity": "sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==", "license": "MIT", "optional": true, "peer": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.2", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, "engines": { "node": ">=0.10.0" } @@ -23258,6 +23085,21 @@ "node": ">=0.10.0" } }, + "node_modules/watchpack-chokidar2/node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/watchpack-chokidar2/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -23377,42 +23219,41 @@ } }, "node_modules/webpack": { - "version": "5.104.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", - "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", + "version": "4.47.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.47.0.tgz", + "integrity": "sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==", "license": "MIT", + "peer": true, "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.4", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" }, "bin": { "webpack": "bin/webpack.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=6.11.5" }, "funding": { "type": "opencollective", @@ -23421,6 +23262,9 @@ "peerDependenciesMeta": { "webpack-cli": { "optional": true + }, + "webpack-command": { + "optional": true } } }, @@ -23566,25 +23410,85 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { - "node": ">=10.13.0" + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "license": "MIT", + "peer": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { - "esrecurse": "^4.3.0", + "esrecurse": "^4.1.0", "estraverse": "^4.1.1" }, "engines": { - "node": ">=8.0.0" + "node": ">=4.0.0" } }, "node_modules/webpack/node_modules/estraverse": { @@ -23592,10 +23496,186 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } }, + "node_modules/webpack/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-descriptor": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz", + "integrity": "sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.2", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/webpack/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "license": "MIT", + "peer": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "license": "MIT", + "peer": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "license": "MIT", + "peer": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -24089,25 +24169,6 @@ "webpack": "^4.4.0 || ^5.9.0" } }, - "node_modules/workbox-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "license": "MIT", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, "node_modules/workbox-window": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", diff --git a/package.json b/package.json index c577c63..1c74b27 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@sncf/bootstrap-sncf.communication": "^4.3.1-r5", + "@sncf/bootstrap-sncf.metier": "^4.3.1-r5", "@supabase/supabase-js": "^2.112.4", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", diff --git a/src/App.css b/src/App.css index 8f4339f..fb5dc23 100644 --- a/src/App.css +++ b/src/App.css @@ -1,5 +1,5 @@ @import "wcs-core/dist/wcs/wcs.css"; -@import "@sncf/bootstrap-sncf.communication/dist/bootstrap-sncf.css"; +@import "@sncf/bootstrap-sncf.metier/dist/bootstrap-sncf.min.css"; /* styles.scss des différents labels SNCF */ @import "wcs-core/design-tokens/dist/sncf-voyageurs.css"; diff --git a/src/App.js b/src/App.js index 684f971..06a8222 100644 --- a/src/App.js +++ b/src/App.js @@ -35,7 +35,13 @@ function App() { } // Synchronisation de session Supabase Auth au démarrage - supabase.auth.getSession().then(({ data: { session } }) => { + supabase.auth.getSession().then(({ data: { session }, error }) => { + if (error) { + console.warn('[App] Session Supabase invalide:', error); + window.localStorage.removeItem('app_user'); + setCurrentUser(null); + return; + } if (session?.user && !window.location.pathname.startsWith('/reinitialiser-mot-de-passe')) { const stored = window.localStorage.getItem('app_user'); if (!stored) { @@ -46,7 +52,11 @@ function App() { } } } - }).catch(() => {}); + }).catch((err) => { + console.warn('[App] Erreur lors de la récupération de session:', err); + window.localStorage.removeItem('app_user'); + setCurrentUser(null); + }); // écouter un événement custom pour synchroniser l'état d'authentification const onAuthChanged = () => { @@ -61,8 +71,13 @@ function App() { const onUnhandledRejection = (event) => { const reason = event?.reason; const message = reason?.message || ''; - if (reason?.name === 'AbortError' || message === 'The operation was aborted.') { - // Certaines annulations navigateur/FileReader ne doivent pas afficher l'overlay runtime. + if ( + reason?.name === 'AbortError' || + message === 'The operation was aborted.' || + reason?.name === 'AuthRetryableFetchError' || + message.includes('Unexpected failure, please check server logs') + ) { + // Certaines annulations navigateur ou retries réseau ne doivent pas bloquer l'UI event.preventDefault(); } }; diff --git a/src/api/depots.js b/src/api/depots.js new file mode 100644 index 0000000..5131e3e --- /dev/null +++ b/src/api/depots.js @@ -0,0 +1,138 @@ +import supabase, { isSupabaseConfigured } from '../supabaseClient'; + +/** + * Récupère la liste complète des dépôts et technicentres enregistrés directement dans la base de données (table depots_technicentres). + */ +export async function getAllDepots() { + if (!isSupabaseConfigured) { + return { data: [], error: { message: 'Supabase non configuré' } }; + } + + try { + const { data, error } = await supabase + .from('depots_technicentres') + .select('*') + .is('deleted_at', null) + .order('nom', { ascending: true }); + + if (error) { + console.error('Erreur chargement depots_technicentres depuis la BDD:', error); + return { data: [], error }; + } + + return { data: data || [], error: null }; + } catch (err) { + console.error('Erreur API getAllDepots:', err); + return { data: [], error: err }; + } +} + +/** + * Récupère un dépôt / technicentre par son identifiant UUID dans la BDD. + */ +export async function getDepotById(id) { + if (!isSupabaseConfigured) return { data: null, error: { message: 'Supabase non configuré' } }; + try { + const { data, error } = await supabase + .from('depots_technicentres') + .select('*') + .eq('id', id) + .single(); + return { data, error }; + } catch (err) { + return { data: null, error: err }; + } +} + +/** + * Crée un nouveau dépôt ou technicentre de maintenance dans Supabase (table depots_technicentres). + */ +export async function createDepot(payload) { + if (!isSupabaseConfigured) return { data: payload, error: null }; + try { + const cleanPayload = { + ...payload, + capacite_max: Number(payload.capacite_max || 20), + specialites: Array.isArray(payload.specialites) + ? payload.specialites + : (typeof payload.specialites === 'string' + ? payload.specialites.split(',').map(s => s.trim()).filter(Boolean) + : []), + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + }; + + const { data, error } = await supabase + .from('depots_technicentres') + .insert([cleanPayload]) + .select('*') + .single(); + + return { data, error }; + } catch (err) { + return { data: null, error: err }; + } +} + +/** + * Met à jour un dépôt / technicentre existant dans Supabase. + */ +export async function updateDepot(id, payload) { + if (!isSupabaseConfigured) return { data: { id, ...payload }, error: null }; + try { + const cleanPayload = { + ...payload, + capacite_max: payload.capacite_max != null ? Number(payload.capacite_max) : undefined, + specialites: payload.specialites !== undefined + ? (Array.isArray(payload.specialites) + ? payload.specialites + : typeof payload.specialites === 'string' + ? payload.specialites.split(',').map(s => s.trim()).filter(Boolean) + : []) + : undefined, + updated_at: new Date().toISOString() + }; + + Object.keys(cleanPayload).forEach(key => cleanPayload[key] === undefined && delete cleanPayload[key]); + + const { data, error } = await supabase + .from('depots_technicentres') + .update(cleanPayload) + .eq('id', id) + .select('*') + .single(); + + return { data, error }; + } catch (err) { + return { data: null, error: err }; + } +} + +/** + * Supprime un dépôt / technicentre de la BDD (table depots_technicentres). + * Détache d'abord les matériels roulants assignés pour éviter toute violation de clé étrangère. + */ +export async function deleteDepot(id) { + if (!isSupabaseConfigured) return { error: null }; + try { + // 1. Détacher les matériels roulants assignés à ce dépôt + try { + await supabase + .from('materiel-roulant') + .update({ depot_id: null, depot_attache: null }) + .eq('depot_id', id); + } catch (e) { + console.warn('Avertissement détachement matériel:', e); + } + + // 2. Supprimer la ligne de la table depots_technicentres + const { error } = await supabase + .from('depots_technicentres') + .delete() + .eq('id', id); + + return { error }; + } catch (err) { + return { error: err }; + } +} diff --git a/src/api/horaires.js b/src/api/horaires.js index 9248139..2e5c854 100644 --- a/src/api/horaires.js +++ b/src/api/horaires.js @@ -518,6 +518,8 @@ export async function updateHoraireComposition(id, compositionTrain = []) { materiel_nom: item?.materiel_nom || '', materiel_nom_technique: item?.materiel_nom_technique || '', materiel_image_url: item?.materiel_image_url || null, + capacite: item?.capacite != null ? Number(item.capacite) : null, + type_train: item?.type_train || '', position: idx + 1 })); @@ -535,6 +537,29 @@ export async function updateHoraireComposition(id, compositionTrain = []) { } } +/** + * Met à jour par lots les compositions de trains optimisées. + * @param {Array<{horaireId: string|number, composition: Array}>} updates + */ +export async function updateHorairesCompositionsBatch(updates = []) { + if (!isSupabaseConfigured) return { data: [], error: null }; + try { + const results = []; + const CHUNK_SIZE = 5; + for (let i = 0; i < updates.length; i += CHUNK_SIZE) { + const chunk = updates.slice(i, i + CHUNK_SIZE); + await Promise.all(chunk.map(async (u) => { + const { data, error } = await updateHoraireComposition(u.horaireId, u.composition); + results.push({ horaireId: u.horaireId, data, error }); + })); + } + return { data: results, error: null }; + } catch (err) { + console.error('Exception updateHorairesCompositionsBatch:', err); + return { data: null, error: err }; + } +} + /** * Supprime un horaire (hard delete) * @param {number|string} id - ID de l'horaire @@ -1162,3 +1187,69 @@ export async function reassignGareInHoraires(sourceId, targetId, targetNom) { return { error: err }; } } + +/** + * Couple deux circulations horaires en unité multiple synchronisée (tranche commune) + * ou découple un train existant. + */ +export async function updateHoraireCouplage(sourceId, sourceNumeroTrain, targetId, targetNumeroTrain) { + if (!isSupabaseConfigured) return { error: null }; + try { + if (targetId) { + // 1. Coupler la source avec la cible + await supabase.from('horaires').update({ + couplage_train_id: targetId, + couplage_numero_train: targetNumeroTrain, + updated_at: new Date() + }).eq('id', sourceId); + + // 2. Coupler réciproquement la cible avec la source + await supabase.from('horaires').update({ + couplage_train_id: sourceId, + couplage_numero_train: sourceNumeroTrain, + updated_at: new Date() + }).eq('id', targetId); + } else { + // Découplage + const { data: current } = await supabase.from('horaires').select('couplage_train_id').eq('id', sourceId).single(); + const oldTargetId = current?.couplage_train_id; + + await supabase.from('horaires').update({ + couplage_train_id: null, + couplage_numero_train: null, + updated_at: new Date() + }).eq('id', sourceId); + + if (oldTargetId) { + await supabase.from('horaires').update({ + couplage_train_id: null, + couplage_numero_train: null, + updated_at: new Date() + }).eq('id', oldTargetId); + } + } + return { error: null }; + } catch (err) { + console.error('Exception updateHoraireCouplage:', err); + return { error: err }; + } +} + +/** + * Enregistre les manœuvres ferroviaires d'une circulation (mise à quai, garage, attelage). + */ +export async function updateHoraireManoeuvres(horaireId, manoeuvres = []) { + if (!isSupabaseConfigured) return { error: null }; + try { + const { data, error } = await supabase + .from('horaires') + .update({ manoeuvres, updated_at: new Date() }) + .eq('id', horaireId) + .select('manoeuvres') + .single(); + return { data: data?.manoeuvres || manoeuvres, error }; + } catch (err) { + console.error('Exception updateHoraireManoeuvres:', err); + return { data: null, error: err }; + } +} diff --git a/src/api/perturbations.js b/src/api/perturbations.js index c880ad9..1321c66 100644 --- a/src/api/perturbations.js +++ b/src/api/perturbations.js @@ -143,32 +143,61 @@ export async function getPerturbationById(id) { /** * Crée une nouvelle perturbation - * @param {Object} payload - Données de la perturbation + * @param {Object} payload - Données de la perturbation (ou objet { payload, modificationsParcours }) * @param {Array} modificationsParcours - Liste des modifications de parcours (optionnel) */ export async function createPerturbation(payload, modificationsParcours = []) { if (!isSupabaseConfigured) { const fakeId = `local-${Date.now()}`; - return { data: { id: fakeId, ...payload }, error: null }; + return { data: { id: fakeId, ...(payload?.payload || payload) }, error: null }; } try { + // Supporter à la fois un payload plat ou l'objet composite retourné par buildPayload ({ payload, modificationsParcours }) + let actualPayload = payload; + let modifs = Array.isArray(modificationsParcours) ? [...modificationsParcours] : []; + + if (payload && typeof payload === 'object' && payload.payload) { + if (modifs.length === 0 && Array.isArray(payload.modificationsParcours)) { + modifs = payload.modificationsParcours; + } + actualPayload = payload.payload; + } else if (payload && Array.isArray(payload.modificationsParcours) && modifs.length === 0) { + modifs = payload.modificationsParcours; + } + // Extraire jours_perturbation et le stocker dans perturbations_arrets (colonne JSONB existante) - const { jours_perturbation, perturbations_arrets, ...rest } = payload; + // Et filtrer strictement les clés qui n'appartiennent pas à la table perturbations + const { + jours_perturbation, + perturbations_arrets, + modificationsParcours: _ignore1, + modifications_parcours: _ignore2, + payload: _ignore3, + scopedRegionId: _ignore4, + ...rest + } = actualPayload || {}; + const perturbationsArretsData = { arrets: perturbations_arrets || [], jours_perturbation: jours_perturbation || null }; + const cleanInsert = { + ...rest, + perturbations_arrets: perturbationsArretsData, + created_at: new Date(), + updated_at: new Date() + }; + delete cleanInsert.modificationsParcours; + delete cleanInsert.modifications_parcours; + delete cleanInsert.payload; + delete cleanInsert.scopedRegionId; + // Créer la perturbation principale const { data: perturbation, error: perturbationError } = await supabase .from('perturbations') - .insert([{ - ...rest, - perturbations_arrets: perturbationsArretsData, - created_at: new Date(), - updated_at: new Date() - }]) + .insert([cleanInsert]) .select('*') .single(); @@ -178,19 +207,22 @@ export async function createPerturbation(payload, modificationsParcours = []) { } // Insérer les modifications de parcours si présentes - if (modificationsParcours.length > 0 && perturbation) { - const modificationsData = modificationsParcours.map(modif => ({ - perturbation_id: perturbation.id, - ...modif - })); + if (modifs && modifs.length > 0 && perturbation) { + try { + const modificationsData = modifs.map(modif => ({ + perturbation_id: perturbation.id, + ...modif + })); - const { error: modifError } = await supabase - .from('perturbations_modifications_parcours') - .insert(modificationsData); + const { error: modifError } = await supabase + .from('perturbations_modifications_parcours') + .insert(modificationsData); - if (modifError) { - console.error('Erreur lors de l\'insertion des modifications de parcours:', modifError); - // On continue même si erreur car la perturbation principale est créée + if (modifError) { + console.warn('Note: insertion perturbations_modifications_parcours non bloquante:', modifError.message || modifError); + } + } catch (errModif) { + console.warn('Note: table perturbations_modifications_parcours non disponible:', errModif.message || errModif); } } @@ -204,30 +236,57 @@ export async function createPerturbation(payload, modificationsParcours = []) { /** * Met à jour une perturbation existante * @param {string} id - ID de la perturbation - * @param {Object} payload - Données à mettre à jour + * @param {Object} payload - Données à mettre à jour (ou objet { payload, modificationsParcours }) * @param {Array} modificationsParcours - Nouvelle liste des modifications de parcours (remplace l'existante) */ export async function updatePerturbation(id, payload, modificationsParcours = null) { if (!isSupabaseConfigured) { - return { data: { id, ...payload }, error: null }; + return { data: { id, ...(payload?.payload || payload) }, error: null }; } try { + let actualPayload = payload; + let modifs = modificationsParcours; + + if (payload && typeof payload === 'object' && payload.payload) { + if (modifs === null && Array.isArray(payload.modificationsParcours)) { + modifs = payload.modificationsParcours; + } + actualPayload = payload.payload; + } else if (payload && Array.isArray(payload.modificationsParcours) && modifs === null) { + modifs = payload.modificationsParcours; + } + // Extraire jours_perturbation et le stocker dans perturbations_arrets (colonne JSONB existante) - const { jours_perturbation, perturbations_arrets, ...rest } = payload; + const { + jours_perturbation, + perturbations_arrets, + modificationsParcours: _ignore1, + modifications_parcours: _ignore2, + payload: _ignore3, + scopedRegionId: _ignore4, + ...rest + } = actualPayload || {}; + const perturbationsArretsData = { arrets: perturbations_arrets || [], jours_perturbation: jours_perturbation || null }; + const cleanUpdate = { + ...rest, + perturbations_arrets: perturbationsArretsData, + updated_at: new Date() + }; + delete cleanUpdate.modificationsParcours; + delete cleanUpdate.modifications_parcours; + delete cleanUpdate.payload; + delete cleanUpdate.scopedRegionId; + // Mettre à jour la perturbation principale const { data: perturbation, error: perturbationError } = await supabase .from('perturbations') - .update({ - ...rest, - perturbations_arrets: perturbationsArretsData, - updated_at: new Date() - }) + .update(cleanUpdate) .eq('id', id) .select('*') .single(); @@ -238,27 +297,29 @@ export async function updatePerturbation(id, payload, modificationsParcours = nu } // Si modifications de parcours fournies, supprimer les anciennes et insérer les nouvelles - if (modificationsParcours !== null && Array.isArray(modificationsParcours)) { - // Supprimer les anciennes modifications - await supabase - .from('perturbations_modifications_parcours') - .delete() - .eq('perturbation_id', id); - - // Insérer les nouvelles modifications - if (modificationsParcours.length > 0) { - const modificationsData = modificationsParcours.map(modif => ({ - perturbation_id: id, - ...modif - })); - - const { error: modifError } = await supabase + if (modifs !== null && Array.isArray(modifs)) { + try { + await supabase .from('perturbations_modifications_parcours') - .insert(modificationsData); + .delete() + .eq('perturbation_id', id); - if (modifError) { - console.error('Erreur lors de la mise à jour des modifications de parcours:', modifError); + if (modifs.length > 0) { + const modificationsData = modifs.map(modif => ({ + perturbation_id: id, + ...modif + })); + + const { error: modifError } = await supabase + .from('perturbations_modifications_parcours') + .insert(modificationsData); + + if (modifError) { + console.warn('Note: mise à jour perturbations_modifications_parcours non bloquante:', modifError.message || modifError); + } } + } catch (errModif) { + console.warn('Note: table perturbations_modifications_parcours non disponible:', errModif.message || errModif); } } diff --git a/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.css b/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.css index c22e8a6..42a205f 100644 --- a/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.css +++ b/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.css @@ -1,453 +1,2106 @@ -.attribution-mr-page { - min-height: calc(100vh - 64px); -} +/* ========================================================================== + ATTRIBUTION MATÉRIEL ROULANT & GESTION DES ROULEMENTS - SNCF MÉTIER PLEIN ÉCRAN + ========================================================================== */ -.attribution-mr-content { +.attribution-fullscreen-page { + min-height: 100vh; + background-color: #f4f6f9; display: flex; flex-direction: column; + padding: 24px 32px 0; + width: 100%; + box-sizing: border-box; +} + +/* 1. EN-TÊTE DE PAGE */ +.attribution-page-header { + display: flex; + justify-content: space-between; + align-items: flex-start; gap: 16px; + flex-wrap: wrap; + margin-bottom: 20px; } -.attribution-mr-header h2 { - margin: 0; +.header-meta { + display: flex; + flex-direction: column; + gap: 4px; } -.attribution-mr-kicker { - margin: 0; - color: #0088ce; - font-size: 12px; +.header-kicker { + font-size: 11px; + font-weight: 800; text-transform: uppercase; - letter-spacing: 0.06em; - font-weight: 700; + letter-spacing: 0.08em; + color: #0088ce; } -.attribution-mr-description { - margin: 6px 0 0; - color: #6b7280; +.header-meta h2 { + margin: 0; + font-size: 26px; + font-weight: 800; + color: #002244; + letter-spacing: -0.02em; } -.attribution-mr-toolbar { +.header-subtitle { + margin: 0; + font-size: 14px; + color: #64748b; + max-width: 850px; +} + +.header-actions { display: flex; align-items: center; gap: 10px; - background: #fff; - border: 1px solid #e5e7eb; - border-radius: 10px; - padding: 12px; + flex-wrap: wrap; } -.attribution-mr-direction-filters { +.auto-config-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border-radius: 6px; + border: 1px solid #cbd5e1; + background: #ffffff; + color: #334155; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s; +} + +.auto-config-btn:hover { + background: #f1f5f9; + border-color: #94a3b8; +} + +.auto-config-btn.is-scheduled { + background: #ecfdf5; + border-color: #10b981; + color: #047857; +} + +.last-refresh-tag { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + border-radius: 999px; + background: #ffffff; + border: 1px solid #e2e8f0; + font-size: 12px; + color: #64748b; + font-weight: 500; +} + +/* TOAST DE NOTIFICATION */ +.attribution-toast { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 18px; + border-radius: 8px; + font-size: 13px; + font-weight: 600; + margin-bottom: 16px; + animation: slideDown 0.3s ease; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); +} + +@keyframes slideDown { + from { opacity: 0; transform: translateY(-8px); } + to { opacity: 1; transform: translateY(0); } +} + +.toast-success { + background: #ecfdf5; + color: #065f46; + border: 1px solid #a7f3d0; +} + +.toast-error { + background: #fef2f2; + color: #991b1b; + border: 1px solid #fecaca; +} + +/* 2. BANDEAU DE KPIS DU PARC */ +.attribution-kpis-strip { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 16px; + margin-bottom: 18px; +} + +.kpi-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 16px; + display: flex; + align-items: center; + gap: 16px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.03); + transition: transform 0.15s, box-shadow 0.15s; +} + +.kpi-card:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06); +} + +.kpi-icon-wrap { + width: 48px; + height: 48px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.icon-blue { background: #e0f2fe; color: #0284c7; } +.icon-green { background: #dcfce7; color: #16a34a; } +.icon-amber { background: #fef3c7; color: #d97706; } +.icon-red { background: #fee2e2; color: #dc2626; } +.icon-teal { background: #ccfbf1; color: #0d9488; } + +.kpi-body { + display: flex; + flex-direction: column; + min-width: 0; + flex: 1; +} + +.kpi-label { + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + color: #64748b; +} + +.kpi-num { + font-size: 24px; + font-weight: 800; + color: #0f172a; + line-height: 1.2; + margin-top: 2px; +} + +.kpi-subtext { + font-size: 11px; + color: #64748b; + margin-top: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.kpi-progress { + height: 6px; + background: #e2e8f0; + border-radius: 999px; + margin-top: 6px; + overflow: hidden; +} + +.kpi-progress-bar { + height: 100%; + background: linear-gradient(90deg, #10b981 0%, #059669 100%); + border-radius: 999px; + transition: width 0.4s ease; +} + +/* 3. WCS TABS NAVIGATION WRAPPER */ +.attribution-tabs-nav-wrapper { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 6px 12px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); + margin-bottom: 18px; +} + +/* 4. BARRE D'OUTILS ET FILTRES D'ATTRIBUTION */ +.attribution-toolbar-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 16px; + display: flex; + flex-direction: column; + gap: 14px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03); + margin-bottom: 18px; +} + +.toolbar-top-row { + display: flex; + align-items: center; + gap: 14px; + flex-wrap: wrap; +} + +.toolbar-control { + display: flex; + align-items: center; + gap: 8px; +} + +.line-select-wrap label { + font-size: 12px; + font-weight: 700; + color: #475569; + text-transform: uppercase; + letter-spacing: 0.04em; + white-space: nowrap; +} + +.custom-select-container select { + padding: 8px 14px; + border-radius: 8px; + border: 1px solid #cbd5e1; + background: #ffffff; + color: #0f172a; + font-size: 13px; + font-weight: 600; + min-width: 260px; + outline: none; + cursor: pointer; + transition: border-color 0.15s; +} + +.custom-select-container select:focus { + border-color: #0088ce; + box-shadow: 0 0 0 3px rgba(0, 136, 206, 0.15); +} + +.direction-pills { + display: flex; + gap: 6px; +} + +.direction-pill { + padding: 6px 12px; + border-radius: 999px; + border: 1px solid #cbd5e1; + background: #f8fafc; + color: #334155; + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s; +} + +.direction-pill:hover { + background: #f1f5f9; +} + +.direction-pill.active { + background: #0088ce; + border-color: #0088ce; + color: #ffffff; +} + +.search-horaire-wrap { + position: relative; + flex: 1; + min-width: 240px; +} + +.search-horaire-wrap wcs-mat-icon { + position: absolute; + left: 10px; + color: #94a3b8; + pointer-events: none; +} + +.search-horaire-wrap input { + width: 100%; + padding: 8px 32px 8px 34px; + border: 1px solid #cbd5e1; + border-radius: 8px; + font-size: 13px; + background: #f8fafc; + outline: none; + transition: all 0.2s; +} + +.search-horaire-wrap input:focus { + background: #ffffff; + border-color: #0088ce; + box-shadow: 0 0 0 3px rgba(0, 136, 206, 0.15); +} + +.clear-search-btn { + position: absolute; + right: 8px; + background: none; + border: none; + color: #94a3b8; + font-size: 16px; + cursor: pointer; +} + +.attribution-view-toggle { + display: flex; + background: #f1f5f9; + border-radius: 8px; + padding: 3px; + margin-left: auto; +} + +.toggle-btn { + display: inline-flex; + align-items: center; + gap: 6px; + border: none; + background: transparent; + color: #64748b; + padding: 6px 12px; + border-radius: 6px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s; +} + +.toggle-btn.active { + background: #ffffff; + color: #0f172a; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.toolbar-bottom-row { + display: flex; + justify-content: space-between; + align-items: center; + border-top: 1px solid #f1f5f9; + padding-top: 12px; + flex-wrap: wrap; + gap: 10px; +} + +.assignment-status-filters { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} + +.filters-label { + font-size: 12px; + font-weight: 600; + color: #64748b; + margin-right: 4px; +} + +.status-filter-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 5px 12px; + border-radius: 999px; + border: 1px solid #e2e8f0; + background: #f8fafc; + color: #334155; + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all 0.15s; +} + +.status-filter-btn:hover { + background: #f1f5f9; +} + +.status-filter-btn.active { + background: #002244; + color: #ffffff; + border-color: #002244; +} + +.status-filter-btn.btn-warning.active { + background: #d97706; + border-color: #d97706; +} + +.status-filter-btn.btn-success.active { + background: #059669; + border-color: #059669; +} + +.status-filter-btn.btn-alt.active { + background: #7c3aed; + border-color: #7c3aed; +} + +.status-filter-btn.btn-danger.active { + background: #dc2626; + border-color: #dc2626; +} + +.btn-count { + background: rgba(0, 0, 0, 0.08); + padding: 1px 6px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; +} + +.status-filter-btn.active .btn-count { + background: rgba(255, 255, 255, 0.25); + color: #ffffff; +} + +.results-counter { + font-size: 12px; + color: #64748b; +} + +/* ÉTAT VIDE / CHARGEMENT */ +.attribution-empty-box { + min-height: 280px; + background: #ffffff; + border: 1px dashed #cbd5e1; + border-radius: 12px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 40px; + color: #64748b; + gap: 12px; +} + +.attribution-empty-box h4 { + margin: 0; + font-size: 16px; + font-weight: 700; + color: #0f172a; +} + +.attribution-empty-box p { + margin: 0; + font-size: 13px; + max-width: 440px; +} + +/* 5. SOUS-VUE A : GRILLE DES CARTES DE CIRCULATION */ +.horaires-attribution-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); + gap: 18px; +} + +.horaire-attribution-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 18px; + display: flex; + flex-direction: column; + gap: 14px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03); + transition: transform 0.2s, box-shadow 0.2s; + position: relative; +} + +.horaire-attribution-card:hover { + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.06); +} + +.horaire-attribution-card.is-unassigned { + border-left: 4px solid #f59e0b; +} + +.horaire-attribution-card.has-anomaly { + border-left: 4px solid #ef4444; +} + +.horaire-card-header { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 8px; +} + +.train-id-badge { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.train-number { + font-size: 16px; + font-weight: 800; + color: #002244; +} + +.train-type-tag { + background: #e0f2fe; + color: #0284c7; + padding: 2px 7px; + border-radius: 4px; + font-size: 11px; + font-weight: 700; +} + +.alternative-variant-badge { + background: #f5f3ff; + color: #6d28d9; + border: 1px solid #ddd6fe; + padding: 2px 6px; + border-radius: 4px; + font-size: 10px; + font-weight: 700; +} + +.mini-alt-tag { + color: #6d28d9; + font-size: 10px; +} + +.train-line-pill { + display: inline-flex; + align-items: center; + gap: 4px; + background: #f1f5f9; + color: #475569; + padding: 3px 8px; + border-radius: 6px; + font-size: 11px; + font-weight: 600; +} + +.horaire-route-box { + display: flex; + align-items: center; + justify-content: space-between; + background: #f8fafc; + border: 1px solid #edf2f7; + border-radius: 8px; + padding: 12px 16px; +} + +.route-step { + display: flex; + flex-direction: column; +} + +.step-destination { + text-align: right; +} + +.step-time { + font-size: 16px; + font-weight: 800; + color: #0f172a; +} + +.step-station { + font-size: 12px; + color: #475569; + margin-top: 2px; +} + +.route-divider { + display: flex; + align-items: center; + justify-content: center; +} + +.route-arrow { + color: #94a3b8; + font-size: 18px; + font-weight: 700; +} + +/* COUPLAGE BANNER */ +.horaire-coupling-banner { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 6px; + background: #f5f3ff; + border: 1px solid #ddd6fe; + color: #5b21b6; + font-size: 12px; + font-weight: 600; +} + +.horaire-anomalies-box { + display: flex; + flex-direction: column; + gap: 6px; +} + +.anomaly-row { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + border-radius: 6px; + font-size: 11px; + font-weight: 600; +} + +.horaire-composition-section { + display: flex; + flex-direction: column; + gap: 8px; +} + +.composition-section-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.comp-title { + font-size: 12px; + font-weight: 700; + color: #475569; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.comp-type-badge { + background: #ecfdf5; + color: #047857; + padding: 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: 700; +} + +.comp-missing-badge { + background: #fef3c7; + color: #b45309; + padding: 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: 700; +} + +.composition-placeholder { + border: 1px dashed #cbd5e1; + border-radius: 8px; + padding: 14px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + color: #94a3b8; + font-size: 12px; + font-weight: 500; + cursor: pointer; + background: #fafafa; + transition: all 0.15s; +} + +.composition-placeholder:hover { + background: #f0f9ff; + border-color: #0088ce; + color: #0088ce; +} + +.composition-units-diagram { + display: flex; + align-items: center; + gap: 8px; + overflow-x: auto; + padding-bottom: 4px; +} + +.diagram-engine { + width: 32px; + height: 38px; + background: #002244; + color: #ffffff; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.diagram-unit-card { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + background: #f1f5f9; + border: 1px solid #cbd5e1; + border-radius: 6px; + min-width: 140px; + flex: 1; +} + +.unit-card-pos { + font-size: 10px; + font-weight: 800; + color: #64748b; + background: #ffffff; + padding: 1px 5px; + border-radius: 4px; +} + +.unit-card-details { + display: flex; + flex-direction: column; + min-width: 0; +} + +.unit-name { + font-size: 12px; + font-weight: 700; + color: #0f172a; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.unit-tech { + font-size: 10px; + color: #64748b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.composition-meta-footer { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 11px; + color: #64748b; + padding-top: 4px; +} + +.meta-cap { + display: inline-flex; + align-items: center; + gap: 4px; + color: #0f172a; +} + +.horaire-card-actions { + display: flex; + align-items: center; + gap: 8px; + margin-top: auto; + padding-top: 10px; + border-top: 1px solid #f1f5f9; + flex-wrap: wrap; +} + +.horaire-card-actions wcs-button { + flex: 1; + min-width: 120px; +} + +/* 6. SOUS-VUE B : FRISE 24H DES SILLONS */ +.horaires-timeline-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03); +} + +.timeline-scroll-container { + max-height: 68vh; + overflow: auto; +} + +.horaires-gantt-table { + --gantt-label-width: 220px; + --gantt-cell-width: 80px; + --gantt-cells-count: 13; + min-width: calc(var(--gantt-label-width) + (var(--gantt-cell-width) * var(--gantt-cells-count))); + width: 100%; +} + +.horaires-gantt-header { + display: grid; + grid-template-columns: var(--gantt-label-width) 1fr; + position: sticky; + top: 0; + z-index: 5; + background: #f1f5f9; + border-bottom: 1px solid #cbd5e1; +} + +.gantt-train-label-head { + padding: 10px 14px; + font-weight: 700; + font-size: 12px; + text-transform: uppercase; + color: #334155; + letter-spacing: 0.05em; + background: #f1f5f9; + position: sticky; + left: 0; + z-index: 6; + border-right: 1px solid #cbd5e1; +} + +.gantt-scale-hours { + display: grid; + grid-template-columns: repeat(var(--gantt-cells-count), 1fr); +} + +.gantt-scale-tick { + padding: 10px 4px; + font-size: 12px; + font-weight: 600; + color: #475569; + text-align: center; + border-left: 1px dashed #cbd5e1; +} + +.horaires-gantt-row { + display: grid; + grid-template-columns: var(--gantt-label-width) 1fr; + min-height: 54px; + border-bottom: 1px solid #e2e8f0; + background: #ffffff; + transition: background 0.15s; +} + +.horaires-gantt-row:hover { + background: #fafcff; +} + +.gantt-train-label-cell { + position: sticky; + left: 0; + z-index: 4; + background: inherit; + border-right: 1px solid #e2e8f0; + padding: 8px 12px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.cell-num { + font-size: 13px; + font-weight: 800; + color: #002244; +} + +.cell-hours { + font-size: 11px; + color: #64748b; + margin-top: 2px; +} + +.gantt-train-track-cell { + position: relative; + min-height: 54px; + background-image: repeating-linear-gradient(to right, transparent 0, transparent calc(100% / var(--gantt-cells-count) - 1px), #f1f5f9 calc(100% / var(--gantt-cells-count) - 1px), #f1f5f9 calc(100% / var(--gantt-cells-count))); +} + +.sillon-timeline-bar { + position: absolute; + top: 50%; + transform: translateY(-50%); + height: 36px; + border: none; + border-radius: 6px; + color: #ffffff; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 10px; + cursor: pointer; + z-index: 2; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); + transition: transform 0.15s; + overflow: hidden; +} + +.sillon-timeline-bar:hover { + transform: translateY(-50%) scale(1.02); + z-index: 3; +} + +.sillon-timeline-bar.bar-nominal { + background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%); +} + +.sillon-timeline-bar.bar-unassigned { + background: linear-gradient(135deg, #d97706 0%, #b45309 100%); +} + +.sillon-timeline-bar.bar-conflict { + background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%); +} + +.sillon-label { + font-size: 11px; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sillon-comp-badge { + font-size: 10px; + background: rgba(0, 0, 0, 0.2); + padding: 1px 6px; + border-radius: 4px; + margin-left: 6px; + white-space: nowrap; +} + +/* 7. MODALE DE COMPOSITION */ +.attribution-builder-modal { + max-width: 1080px; + width: 95%; +} + +.modal-header-meta { + display: flex; + flex-direction: column; + gap: 2px; +} + +.modal-kicker { + font-size: 10px; + font-weight: 800; + color: #0088ce; + letter-spacing: 0.08em; +} + +.modal-header-meta h3 { + margin: 0; + font-size: 20px; + font-weight: 800; + color: #002244; +} + +.modal-route-info { + font-size: 12px; + color: #475569; +} + +.modal-tabs-header { + display: flex; + gap: 8px; + margin-left: auto; + margin-right: 16px; +} + +.modal-tab-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 14px; + border-radius: 6px; + border: 1px solid #cbd5e1; + background: #f8fafc; + color: #334155; + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s; +} + +.modal-tab-btn.active { + background: #0088ce; + border-color: #0088ce; + color: #ffffff; +} + +.builder-modal-body { + display: flex; + flex-direction: column; + gap: 16px; + max-height: 72vh; + overflow-y: auto; + padding: 18px 24px; +} + +.builder-alert-banner { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px 16px; + border-radius: 8px; + background: #fef2f2; + border: 1px solid #fecaca; + color: #991b1b; + font-size: 12px; +} + +.builder-alert-banner ul { + margin: 4px 0 0; + padding-left: 18px; +} + +.builder-columns-layout { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; +} + +.builder-draft-panel, +.builder-catalog-panel { + display: flex; + flex-direction: column; + gap: 12px; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 10px; + padding: 16px; +} + +.panel-section-head { + display: flex; + justify-content: space-between; + align-items: center; +} + +.panel-section-head h4 { + margin: 0; + font-size: 14px; + font-weight: 800; + color: #0f172a; +} + +.panel-sub { + font-size: 11px; + color: #64748b; +} + +.draft-capacity-tag { + display: inline-flex; + align-items: center; + gap: 4px; + background: #ecfdf5; + color: #047857; + padding: 4px 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; +} + +.draft-empty-state { + text-align: center; + padding: 40px 20px; + border: 1px dashed #cbd5e1; + border-radius: 8px; + color: #64748b; + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} + +.draft-items-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.draft-item-card { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); +} + +.draft-item-position { + display: flex; + flex-direction: column; + align-items: center; + min-width: 32px; +} + +.pos-badge { + font-size: 12px; + font-weight: 800; + color: #002244; +} + +.pos-label { + font-size: 9px; + color: #64748b; + text-transform: uppercase; +} + +.draft-item-avatar { + width: 44px; + height: 38px; + border-radius: 6px; + background: #f1f5f9; + border: 1px solid #e2e8f0; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + flex-shrink: 0; +} + +.draft-item-avatar img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.draft-item-meta { + flex: 1; + min-width: 0; +} + +.item-name { + display: block; + font-size: 13px; + font-weight: 700; + color: #0f172a; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.item-tech { + display: block; + font-size: 11px; + color: #64748b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.item-pills { + display: flex; + gap: 4px; + margin-top: 2px; +} + +.pill-type { + background: #e0f2fe; + color: #0369a1; + font-size: 10px; + font-weight: 700; + padding: 1px 5px; + border-radius: 4px; +} + +.pill-cap { + background: #ecfdf5; + color: #047857; + font-size: 10px; + padding: 1px 5px; + border-radius: 4px; +} + +.pill-depot { + background: #fef3c7; + color: #92400e; + font-size: 10px; + padding: 1px 5px; + border-radius: 4px; +} + +.draft-item-controls { + display: flex; + gap: 4px; +} + +.control-btn { + background: #ffffff; + border: 1px solid #cbd5e1; + border-radius: 6px; + color: #334155; + padding: 4px 8px; + font-size: 12px; + font-weight: 700; + cursor: pointer; + transition: all 0.15s; +} + +.control-btn:hover:not(:disabled) { + background: #f1f5f9; + border-color: #94a3b8; +} + +.control-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.control-btn.delete { + color: #dc2626; + border-color: #fca5a5; +} + +.control-btn.delete:hover:not(:disabled) { + background: #fee2e2; +} + +/* CATALOGUE DE MATÉRIEL */ +.catalog-status-tabs { display: flex; gap: 6px; flex-wrap: wrap; } -.attribution-mr-direction-btn { - border: 1px solid #d1d5db; - border-radius: 999px; - background: #f8fafc; - color: #1f2937; - font-size: 13px; - padding: 6px 12px; - cursor: pointer; -} - -.attribution-mr-direction-btn.is-active { - background: #0088ce; - border-color: #0088ce; - color: #fff; -} - -.attribution-mr-direction-btn:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.attribution-mr-toolbar label { - font-weight: 600; -} - -.attribution-mr-toolbar select { - min-width: 280px; - padding: 8px 10px; - border-radius: 8px; - border: 1px solid #d1d5db; - font-size: 14px; - font-family: inherit; -} - -.attribution-mr-count { - margin-left: auto; - color: #6b7280; - font-size: 13px; -} - -.attribution-mr-timeline-wrap { - background: #fff; - border: 1px solid #e5e7eb; - border-radius: 12px; - padding: 18px; - min-height: 280px; -} - -.attribution-mr-empty { - min-height: 230px; - display: flex; - flex-direction: column; +.catalog-tab-btn { + display: inline-flex; align-items: center; - justify-content: center; - color: #9ca3af; - gap: 10px; - text-align: center; + gap: 6px; + padding: 5px 10px; + border-radius: 999px; + border: 1px solid #cbd5e1; + background: #ffffff; + color: #475569; + font-size: 11px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s; } -.attribution-mr-gantt-shell { - border: 1px solid #e5e7eb; - border-radius: 10px; - background: #f8fafc; +.catalog-tab-btn.active { + background: #002244; + border-color: #002244; + color: #ffffff; } -.attribution-mr-gantt-scroll { - max-height: 62vh; - overflow: auto; +.tab-count { + background: rgba(0, 0, 0, 0.08); + padding: 1px 5px; + border-radius: 999px; + font-size: 10px; } -.attribution-mr-gantt { - --gantt-cell-width: 230px; - --gantt-tick-count: 13; - min-width: calc(220px + (var(--gantt-cell-width) * var(--gantt-tick-count))); - width: max-content; +.catalog-tab-btn.active .tab-count { + background: rgba(255, 255, 255, 0.25); + color: #ffffff; } -.attribution-mr-gantt-header { - display: grid; - grid-template-columns: 220px 1fr; - position: sticky; - top: 0; - z-index: 3; - background: #f1f5f9; - border-bottom: 1px solid #dbe3ed; +.catalog-search-bar { + position: relative; + display: flex; + align-items: center; } -.attribution-mr-gantt-label-head { - padding: 10px 12px; - font-weight: 700; - border-right: 1px solid #dbe3ed; - background: #f1f5f9; - position: sticky; - left: 0; - z-index: 4; +.catalog-search-bar wcs-mat-icon { + position: absolute; + left: 8px; + color: #94a3b8; + pointer-events: none; } -.attribution-mr-gantt-scale { - display: grid; - grid-template-columns: repeat(var(--gantt-tick-count), var(--gantt-cell-width)); - width: max-content; +.catalog-search-bar input { + width: 100%; + padding: 6px 12px 6px 30px; + border-radius: 6px; + border: 1px solid #cbd5e1; + font-size: 12px; + outline: none; + background: #ffffff; } -.attribution-mr-gantt-tick { - padding: 12px 8px; - font-size: 13px; - color: #334155; - text-align: center; - border-left: 1px dashed #dbe3ed; -} - -.attribution-mr-gantt-row { - display: grid; - grid-template-columns: 220px 1fr; - min-height: 66px; - border-bottom: 1px solid #e5e7eb; - background: #fff; -} - -.attribution-mr-gantt-label { - padding: 8px 12px; - border-right: 1px solid #e2e8f0; - background: #fff; - position: sticky; - left: 0; - z-index: 2; +.catalog-items-grid { display: flex; flex-direction: column; - justify-content: center; + gap: 8px; + max-height: 380px; + overflow-y: auto; } -.attribution-mr-gantt-label small { +.catalog-empty-notice { + text-align: center; + padding: 30px 10px; color: #64748b; font-size: 12px; } -.attribution-mr-gantt-track { - position: relative; - min-height: 66px; - background-image: repeating-linear-gradient(to right, #f8fafc 0, #f8fafc calc(var(--gantt-cell-width) - 1px), #dbe3ed calc(var(--gantt-cell-width) - 1px), #dbe3ed var(--gantt-cell-width)); -} - -.attribution-mr-gantt-bar { - position: absolute; - top: 50%; - transform: translateY(-50%); - height: 42px; - border: none; - border-radius: 6px; - background: linear-gradient(90deg, #3b82f6, #2563eb); - color: #fff; - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 0 12px; - text-align: left; - cursor: pointer; - box-shadow: 0 2px 8px rgba(37, 99, 235, 0.2); -} - -.attribution-mr-gantt-bar span, -.attribution-mr-gantt-bar em { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-style: normal; - font-size: 13px; -} - -.attribution-mr-modal { - max-width: 940px; -} - -.attribution-mr-modal-body { - display: flex; - flex-direction: column; - gap: 18px; -} - -.attribution-mr-modal-subtitle { - color: #6b7280; - font-size: 14px; -} - -.attribution-mr-section-head { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; - color: #6b7280; - font-size: 13px; -} - -.attribution-mr-draft-empty { - border: 1px dashed #d1d5db; - border-radius: 8px; - padding: 14px; - text-align: center; - color: #9ca3af; -} - -.attribution-mr-draft-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.attribution-mr-draft-item { +.catalog-materiel-card { display: flex; align-items: center; gap: 10px; - border: 1px solid #e5e7eb; + padding: 8px 12px; + background: #ffffff; + border: 1px solid #e2e8f0; border-radius: 8px; - padding: 8px 10px; + transition: all 0.15s; } -.attribution-mr-pos { - min-width: 30px; - font-weight: 700; - color: #374151; -} - -.attribution-mr-draft-main { - flex: 1; - display: flex; - flex-direction: column; -} - -.attribution-mr-draft-main small { - color: #9ca3af; -} - -.attribution-mr-draft-actions { - display: flex; - gap: 6px; -} - -.attribution-mr-draft-actions button { - border: 1px solid #d1d5db; - border-radius: 6px; - background: #fff; - padding: 4px 8px; - cursor: pointer; -} - -.attribution-mr-materiels-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); - gap: 10px; -} - -.attribution-mr-materiels-groups { - display: flex; - flex-direction: column; - gap: 14px; -} - -.attribution-mr-materiels-group { - border: 1px solid #e5e7eb; - border-radius: 8px; - padding: 10px; +.catalog-materiel-card.is-disabled { + opacity: 0.55; background: #f8fafc; } -.attribution-mr-materiels-group-title { - font-weight: 700; - color: #1e293b; - margin-bottom: 8px; +.catalog-materiel-card.is-elsewhere { + border-left: 3px solid #f97316; } -.attribution-mr-materiel-card { - border: 1px solid #e5e7eb; - background: #fff; +.card-avatar { + width: 44px; + height: 38px; + border-radius: 6px; + background: #f1f5f9; + border: 1px solid #e2e8f0; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + flex-shrink: 0; +} + +.card-avatar img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.card-details { + flex: 1; + min-width: 0; +} + +.mat-nom { + display: block; + font-size: 13px; + font-weight: 700; + color: #0f172a; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.mat-tech { + display: block; + font-size: 11px; + color: #64748b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.card-pills { + display: flex; + gap: 4px; + margin-top: 2px; +} + +.status-note { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 11px; + font-weight: 600; + margin-top: 4px; +} + +.note-green { color: #059669; } +.note-orange { color: #d97706; } +.note-red { color: #dc2626; } + +/* 8. MODALE COUPLAGE AVEC UN AUTRE TRAIN */ +.coupling-modal { + max-width: 660px; +} + +.coupling-source-card { + background: #f8fafc; + border: 1px solid #e2e8f0; border-radius: 8px; - padding: 10px; + padding: 12px 14px; + margin-bottom: 16px; +} + +.coupling-source-header { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 14px; + font-weight: 700; + color: #002244; +} + +.coupling-source-comp { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 12px; + color: #64748b; + margin-top: 4px; +} + +.already-coupled-tag { + background: #f5f3ff; + color: #6d28d9; + padding: 2px 6px; + border-radius: 4px; + font-weight: 700; +} + +.coupling-label { + display: block; + font-size: 12px; + font-weight: 700; + color: #334155; + margin-bottom: 8px; + text-transform: uppercase; +} + +.coupling-candidates-list { display: flex; flex-direction: column; + gap: 8px; + max-height: 280px; + overflow-y: auto; + margin-bottom: 14px; +} + +.coupling-candidate-item { + display: flex; align-items: center; - gap: 6px; + gap: 12px; + padding: 10px 14px; + border: 1px solid #e2e8f0; + border-radius: 8px; + background: #ffffff; cursor: pointer; + transition: all 0.15s; } -.attribution-mr-materiel-card img { - width: 100%; - max-height: 52px; - object-fit: contain; +.coupling-candidate-item:hover { + border-color: #0088ce; + background: #f8fafc; } -.attribution-mr-materiel-card span { - font-size: 12px; - text-align: center; +.coupling-candidate-item.is-selected { + border-color: #7c3aed; + background: #faf5ff; } -.attribution-mr-readonly { - color: #9ca3af; - margin: 0; +.candidate-item-label { + flex: 1; } -.attribution-mr-error { - margin: 0; - color: #dc2626; +.cand-top-row { + display: flex; + justify-content: space-between; + align-items: center; font-size: 13px; } -@media (max-width: 768px) { - .attribution-mr-toolbar { +.cand-dest { + color: #002244; + font-weight: 600; +} + +.cand-sub-row { + display: flex; + justify-content: space-between; + font-size: 11px; + color: #64748b; + margin-top: 2px; +} + +.coupling-empty-candidates { + padding: 24px; + text-align: center; + color: #64748b; + border: 1px dashed #cbd5e1; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + font-size: 12px; +} + +.coupling-info-box { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 10px 12px; + background: #f0f9ff; + border: 1px solid #bae6fd; + border-radius: 8px; + color: #0369a1; + font-size: 12px; +} + +/* 9. MODALE ATTRIBUTION AUTOMATIQUE */ +.auto-config-modal { + max-width: 580px; +} + +.config-section { + display: flex; + flex-direction: column; + gap: 16px; + margin-bottom: 16px; +} + +.config-toggle-row { + display: flex; + justify-content: space-between; + align-items: center; +} + +.config-desc { + margin: 2px 0 0; + font-size: 12px; + color: #64748b; + max-width: 380px; +} + +.switch-container { + position: relative; + display: inline-block; + width: 44px; + height: 24px; +} + +.switch-container input { + opacity: 0; + width: 0; + height: 0; +} + +.switch-slider { + position: absolute; + cursor: pointer; + inset: 0; + background-color: #cbd5e1; + transition: 0.3s; + border-radius: 24px; +} + +.switch-slider:before { + position: absolute; + content: ""; + height: 18px; + width: 18px; + left: 3px; + bottom: 3px; + background-color: white; + transition: 0.3s; + border-radius: 50%; +} + +.switch-container input:checked + .switch-slider { + background-color: #0088ce; +} + +.switch-container input:checked + .switch-slider:before { + transform: translateX(20px); +} + +.config-time-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 14px; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 8px; +} + +.config-time-input { + padding: 6px 12px; + border-radius: 6px; + border: 1px solid #cbd5e1; + font-size: 14px; + font-weight: 700; + outline: none; +} + +.config-rules-box { + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 8px; + padding: 12px 16px; + margin-bottom: 14px; +} + +.config-rules-box h4 { + margin: 0 0 6px; + font-size: 12px; + font-weight: 700; + color: #334155; + text-transform: uppercase; +} + +.config-rules-box ul { + margin: 0; + padding-left: 18px; + font-size: 12px; + color: #475569; + display: flex; + flex-direction: column; + gap: 4px; +} + +.config-last-run-box { + display: flex; + flex-direction: column; + gap: 2px; + padding: 10px 14px; + background: #ecfdf5; + border: 1px solid #a7f3d0; + border-radius: 8px; + color: #065f46; + font-size: 12px; +} + +/* 10. PIED DE PAGE CORPORATE WCS */ +.sncf-page-footer { + margin-top: 40px; + border-top: 1px solid #e2e8f0; +} + +.footer-sncf-badge { + width: 36px; + height: 36px; + border-radius: 8px; + background: #002244; + display: flex; + align-items: center; + justify-content: center; +} + +/* RESPONSIVE */ +@media (max-width: 900px) { + .attribution-fullscreen-page { + padding: 16px; + } + + .builder-columns-layout { + grid-template-columns: 1fr; + } + + .attribution-toolbar-card .toolbar-top-row { flex-direction: column; align-items: stretch; } - .attribution-mr-toolbar select { + .custom-select-container select { min-width: 0; width: 100%; } - .attribution-mr-count { + .attribution-view-toggle { margin-left: 0; } - - .attribution-mr-gantt-header, - .attribution-mr-gantt-row { - grid-template-columns: 170px 1fr; - } - - .attribution-mr-gantt-label-head, - .attribution-mr-gantt-label { - min-width: 170px; - } } -/* Dark Mode */ -body.dark-mode .attribution-mr-page, -[data-theme="dark"] .attribution-mr-page { +/* DARK MODE */ +body.dark-mode .attribution-fullscreen-page, +[data-theme="dark"] .attribution-fullscreen-page { background-color: #0b111e; color: #f1f5f9; } -body.dark-mode .attribution-mr-header h2, -[data-theme="dark"] .attribution-mr-header h2 { +body.dark-mode .header-meta h2, +[data-theme="dark"] .header-meta h2 { color: #f8fafc; } -body.dark-mode .attribution-mr-description, -[data-theme="dark"] .attribution-mr-description { - color: #94a3b8; -} - -body.dark-mode .attribution-mr-toolbar, -body.dark-mode .attribution-mr-timeline-wrap, -[data-theme="dark"] .attribution-mr-toolbar, -[data-theme="dark"] .attribution-mr-timeline-wrap { +body.dark-mode .kpi-card, +body.dark-mode .attribution-tabs-nav-wrapper, +body.dark-mode .attribution-toolbar-card, +body.dark-mode .horaire-attribution-card, +body.dark-mode .horaires-timeline-card, +body.dark-mode .last-refresh-tag, +body.dark-mode .auto-config-btn, +[data-theme="dark"] .kpi-card, +[data-theme="dark"] .attribution-tabs-nav-wrapper, +[data-theme="dark"] .attribution-toolbar-card, +[data-theme="dark"] .horaire-attribution-card, +[data-theme="dark"] .horaires-timeline-card, +[data-theme="dark"] .last-refresh-tag, +[data-theme="dark"] .auto-config-btn { background: #151f30; border-color: #243247; color: #f1f5f9; } -body.dark-mode .attribution-mr-direction-btn, -[data-theme="dark"] .attribution-mr-direction-btn { +body.dark-mode .kpi-num, +body.dark-mode .train-number, +body.dark-mode .step-time, +body.dark-mode .cell-num, +body.dark-mode .unit-name, +body.dark-mode .mat-nom, +[data-theme="dark"] .kpi-num, +[data-theme="dark"] .train-number, +[data-theme="dark"] .step-time, +[data-theme="dark"] .cell-num, +[data-theme="dark"] .unit-name, +[data-theme="dark"] .mat-nom { + color: #f8fafc; +} + +body.dark-mode .custom-select-container select, +body.dark-mode .search-horaire-wrap input, +body.dark-mode .catalog-search-bar input, +body.dark-mode .config-time-input, +[data-theme="dark"] .custom-select-container select, +[data-theme="dark"] .search-horaire-wrap input, +[data-theme="dark"] .catalog-search-bar input, +[data-theme="dark"] .config-time-input { background: #0b111e; border-color: #243247; - color: #cbd5e1; + color: #f1f5f9; } -body.dark-mode .attribution-mr-direction-btn.is-active, -[data-theme="dark"] .attribution-mr-direction-btn.is-active { - background: #0284c7; - border-color: #0284c7; - color: #ffffff; -} - -body.dark-mode .attribution-mr-gantt-header, -[data-theme="dark"] .attribution-mr-gantt-header { +body.dark-mode .horaire-route-box, +body.dark-mode .builder-draft-panel, +body.dark-mode .builder-catalog-panel, +body.dark-mode .coupling-source-card, +body.dark-mode .config-rules-box, +body.dark-mode .config-time-row, +body.dark-mode .horaires-gantt-header, +body.dark-mode .gantt-train-label-head, +[data-theme="dark"] .horaire-route-box, +[data-theme="dark"] .builder-draft-panel, +[data-theme="dark"] .builder-catalog-panel, +[data-theme="dark"] .coupling-source-card, +[data-theme="dark"] .config-rules-box, +[data-theme="dark"] .config-time-row, +[data-theme="dark"] .horaires-gantt-header, +[data-theme="dark"] .gantt-train-label-head { background: #0e1726; border-color: #243247; } -body.dark-mode .attribution-mr-gantt-row, -[data-theme="dark"] .attribution-mr-gantt-row { - border-bottom-color: #1e293b; -} - -body.dark-mode .attribution-mr-gantt-label, -[data-theme="dark"] .attribution-mr-gantt-label { +body.dark-mode .draft-item-card, +body.dark-mode .catalog-materiel-card, +body.dark-mode .coupling-candidate-item, +[data-theme="dark"] .draft-item-card, +[data-theme="dark"] .catalog-materiel-card, +[data-theme="dark"] .coupling-candidate-item { background: #111a28; - border-right-color: #243247; - color: #f8fafc; -} - -body.dark-mode .attribution-mr-materiels-group, -[data-theme="dark"] .attribution-mr-materiels-group { - background: #111a28; - border-color: #243247; -} - -body.dark-mode .attribution-mr-materiels-group-title, -[data-theme="dark"] .attribution-mr-materiels-group-title { - color: #f8fafc; -} - -body.dark-mode .attribution-mr-materiel-card, -[data-theme="dark"] .attribution-mr-materiel-card { - background: #151f30; border-color: #243247; color: #f1f5f9; } +/* ========================================================================== + SERVICES ROUTIERS (CARS & AUTOCARS) + ========================================================================== */ +.comp-road-badge { + background: #fef3c7 !important; + color: #92400e !important; + border: 1px solid #fde68a !important; + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + border-radius: 4px; + font-weight: 700; + font-size: 11px; +} + +.road-service-box { + background: #fffbeb !important; + border: 1px dashed #fcd34d !important; + color: #78350f !important; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px; + border-radius: 8px; + font-size: 13px; + font-weight: 500; +} + +.road-service-pill-notice { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: #92400e; + background: #fef3c7; + padding: 6px 12px; + border-radius: 6px; + font-weight: 600; + border: 1px solid #fde68a; +} + +.status-filter-btn.btn-road { + background: #fffbeb; + color: #92400e; + border-color: #fde68a; +} + +.status-filter-btn.btn-road.active { + background: #fef3c7; + border-color: #d97706; + color: #78350f; + box-shadow: 0 0 0 1px #d97706; +} + +.depots-empty-card { + background: #ffffff; + border: 1px dashed #cbd5e1; + border-radius: 12px; + padding: 40px 20px; + text-align: center; + color: #64748b; + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} + +.depots-empty-card h4 { + margin: 8px 0 4px; + color: #1e293b; + font-size: 16px; + font-weight: 700; +} + +body.dark-mode .comp-road-badge, +[data-theme="dark"] .comp-road-badge { + background: #451a03 !important; + color: #fef3c7 !important; + border-color: #78350f !important; +} + +body.dark-mode .road-service-box, +[data-theme="dark"] .road-service-box { + background: #1e1b12 !important; + border-color: #78350f !important; + color: #fef3c7 !important; +} + +body.dark-mode .road-service-pill-notice, +[data-theme="dark"] .road-service-pill-notice { + background: #271f11; + border-color: #78350f; + color: #fef3c7; +} + +body.dark-mode .depots-empty-card, +[data-theme="dark"] .depots-empty-card { + background: #151f30; + border-color: #243247; + color: #94a3b8; +} + +body.dark-mode .depots-empty-card h4, +[data-theme="dark"] .depots-empty-card h4 { + color: #f8fafc; +} + +/* ========================================================================= + 11. STYLES POUR LA MODALE D'OPTIMISATION DU ROULEMENT + ========================================================================= */ +.btn-opt-roster { + background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%) !important; + box-shadow: 0 2px 8px rgba(3, 105, 161, 0.25); + transition: transform 0.15s ease, box-shadow 0.15s ease; +} + +.btn-opt-roster:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(3, 105, 161, 0.35); +} + +.optimization-modal { + max-width: 860px; + width: 90vw; +} + +.opt-settings-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 12px; +} + +.opt-setting-card { + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 8px; + padding: 12px 14px; +} + +.opt-setting-card strong { + font-size: 13px; + color: #0f172a; +} + +.opt-preview-section { + background: #f1f5f9; + border: 1px solid #cbd5e1; + border-radius: 10px; + padding: 16px; +} + +.opt-section-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 700; + color: #0f172a; + margin: 0 0 12px 0; +} + +.opt-stats-comparison-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); + gap: 10px; +} + +.opt-stat-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 8px; + padding: 12px; + text-align: center; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); +} + +.stat-label { + display: block; + font-size: 11px; + text-transform: uppercase; + font-weight: 700; + color: #64748b; + margin-bottom: 4px; +} + +.stat-diff-row { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + font-size: 18px; + font-weight: 800; + color: #0f172a; +} + +.stat-val.before { + color: #94a3b8; + font-size: 15px; +} + +.stat-val.after { + font-weight: 800; +} + +.stat-total { + font-size: 13px; + color: #64748b; + font-weight: 600; +} + +.stat-sub { + display: block; + font-size: 11px; + color: #64748b; + margin-top: 4px; +} + +.opt-actions-log-box { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 8px; + overflow: hidden; +} + +.log-box-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 14px; + background: #f8fafc; + border-bottom: 1px solid #e2e8f0; + font-size: 12px; +} + +.log-box-body { + max-height: 180px; + overflow-y: auto; + padding: 8px 12px; + font-size: 12px; + display: flex; + flex-direction: column; + gap: 6px; +} + +.log-row { + display: flex; + align-items: center; + color: #334155; + line-height: 1.4; +} + +/* Dark mode pour la modale d'optimisation */ +body.dark-mode .opt-setting-card, +[data-theme="dark"] .opt-setting-card { + background: #1e293b; + border-color: #334155; +} + +body.dark-mode .opt-setting-card strong, +[data-theme="dark"] .opt-setting-card strong { + color: #f8fafc; +} + +body.dark-mode .opt-preview-section, +[data-theme="dark"] .opt-preview-section { + background: #0f172a; + border-color: #334155; +} + +body.dark-mode .opt-section-title, +[data-theme="dark"] .opt-section-title { + color: #f8fafc; +} + +body.dark-mode .opt-stat-card, +[data-theme="dark"] .opt-stat-card { + background: #1e293b; + border-color: #334155; +} + +body.dark-mode .stat-diff-row, +[data-theme="dark"] .stat-diff-row { + color: #f8fafc; +} + +body.dark-mode .opt-actions-log-box, +[data-theme="dark"] .opt-actions-log-box { + background: #1e293b; + border-color: #334155; +} + +body.dark-mode .log-box-header, +[data-theme="dark"] .log-box-header { + background: #0f172a; + border-color: #334155; +} + +body.dark-mode .log-row, +[data-theme="dark"] .log-row { + color: #cbd5e1; +} diff --git a/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.js b/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.js index 02955da..a29d7b3 100644 --- a/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.js +++ b/src/pages/attribution-materiel-roulant/AttributionMaterielRoulant.js @@ -1,9 +1,30 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; import { getAllLignes } from '../../api/lignes'; -import { getAllHoraires, updateHoraireComposition, formatHeure } from '../../api/horaires'; -import { getAllMateriels } from '../../api/materielsRoulants'; +import { + getAllHoraires, + updateHoraireComposition, + updateHorairesCompositionsBatch, + updateHoraireCouplage, + updateQuaisHoraire, + updateQuaisBatch, + formatHeure +} from '../../api/horaires'; +import { getAllMateriels, updateMateriel } from '../../api/materielsRoulants'; +import { getAllDepots } from '../../api/depots'; +import { getParametre, upsertParametre } from '../../api/parametres'; import { canPerform, getCurrentUser, getScopedRegionId } from '../../utils/userUtils'; -import { buildMaterielTimeline, getMaterielStatusAtTime } from '../../utils/roulementUtils'; +import { + buildMaterielTimeline, + getMaterielStatusAtTime, + analyzeHoraireComposition, + analyzeGlobalRoster, + normalizeStation, + groupHorairesWithAlternatives, + runAutoAttribution, + optimizeMaterielRosters, + evaluateQuaiContinuity, + isRoadService +} from '../../utils/roulementUtils'; import { timeToMinutes } from '../../utils/quaisUtils'; import VueRoulement from './VueRoulement'; import './AttributionMaterielRoulant.css'; @@ -11,18 +32,9 @@ import './AttributionMaterielRoulant.css'; const DAY_MINUTES = 24 * 60; const TIME_MARKERS_HOURS = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]; -function parseTimeToMinutes(value) { - if (!value || typeof value !== 'string') return null; - const [h, m] = value.split(':'); - const hours = Number(h); - const minutes = Number(m); - if (Number.isNaN(hours) || Number.isNaN(minutes)) return null; - return (hours * 60) + minutes; -} - function getBarPlacement(horaire) { - const depart = parseTimeToMinutes(horaire?.heure_depart); - const arriveeRaw = parseTimeToMinutes(horaire?.heure_arrivee); + const depart = timeToMinutes(horaire?.heure_depart); + const arriveeRaw = timeToMinutes(horaire?.heure_arrivee); if (depart === null) { return { leftPct: 0, widthPct: 8 }; @@ -42,32 +54,33 @@ function sortByHoraire(a, b) { return String(a?.numero_train || '').localeCompare(String(b?.numero_train || '')); } -function toDraftComposition(compositionTrain) { +function toDraftComposition(compositionTrain, allMateriels = []) { if (!Array.isArray(compositionTrain)) return []; return [...compositionTrain] .sort((a, b) => Number(a?.position || 0) - Number(b?.position || 0)) - .map((item, idx) => ({ - local_id: item?.local_id || `draft-${Date.now()}-${idx}-${Math.random()}`, - materiel_id: item?.materiel_id, - materiel_nom: item?.materiel_nom || '', - materiel_nom_technique: item?.materiel_nom_technique || '', - materiel_image_url: item?.materiel_image_url || null, - position: idx + 1 - })); -} - -function normalizeStationName(value) { - return String(value || '').trim().toLowerCase(); + .map((item, idx) => { + const fullMat = allMateriels.find(m => String(m.id) === String(item?.materiel_id)); + return { + local_id: item?.local_id || `draft-${Date.now()}-${idx}-${Math.random()}`, + materiel_id: item?.materiel_id, + materiel_nom: item?.materiel_nom || fullMat?.nom || '', + materiel_nom_technique: item?.materiel_nom_technique || fullMat?.nom_technique || '', + materiel_image_url: item?.materiel_image_url || fullMat?.image_url || null, + capacite: item?.capacite != null ? Number(item.capacite) : (fullMat?.capacite ? Number(fullMat.capacite) : 0), + type_train: item?.type_train || fullMat?.type_train || '', + position: idx + 1 + }; + }); } function matchesDirection(horaire, ligne, direction) { if (!ligne || direction === 'all') return true; const lineDepartId = ligne?.gare_depart_id || ligne?.gare_depart?.id || null; - const lineDepartName = normalizeStationName(ligne?.gare_depart?.nom || ligne?.gare_depart_nom); + const lineDepartName = normalizeStation(ligne?.gare_depart?.nom || ligne?.gare_depart_nom); const horaireDepartId = horaire?.gare_depart_id || null; const horaireArriveeId = horaire?.gare_arrivee_id || null; - const horaireDepartName = normalizeStationName(horaire?.gare_depart_nom); - const horaireArriveeName = normalizeStationName(horaire?.gare_arrivee_nom); + const horaireDepartName = normalizeStation(horaire?.gare_depart_nom); + const horaireArriveeName = normalizeStation(horaire?.gare_arrivee_nom); if (direction === 'aller') { return (lineDepartId && String(horaireDepartId) === String(lineDepartId)) @@ -79,48 +92,118 @@ function matchesDirection(horaire, ligne, direction) { } export default function AttributionMaterielRoulant() { - // Evite de recréer l'objet utilisateur à chaque rendu (sinon useEffect repart en boucle) const currentUser = useMemo(() => getCurrentUser(), []); const canUpdate = canPerform(currentUser, 'horaires', 'update'); + // Données de base const [lignes, setLignes] = useState([]); const [horaires, setHoraires] = useState([]); const [materiels, setMateriels] = useState([]); - const [activeTab, setActiveTab] = useState('liste'); + const [depots, setDepots] = useState([]); + const [loading, setLoading] = useState(true); + const [lastRefreshed, setLastRefreshed] = useState(new Date()); + // Onglet principal ('attribution' | 'roulements') + const [activeTab, setActiveTab] = useState('attribution'); + const tabsRef = useRef(null); + + // Filtres Onglet 1 (Attribution) const [selectedLigneId, setSelectedLigneId] = useState(''); const [directionFilter, setDirectionFilter] = useState('all'); + const [assignmentFilter, setAssignmentFilter] = useState('all'); // 'all' | 'unassigned' | 'assigned' | 'anomaly' | 'alternatives' + const [searchHoraireTerm, setSearchHoraireTerm] = useState(''); + const [attributionViewMode, setAttributionViewMode] = useState('cards'); // 'cards' | 'timeline' + + // Modale Composition const [selectedHoraire, setSelectedHoraire] = useState(null); const [compositionDraft, setCompositionDraft] = useState([]); - const [modalActiveTab, setModalActiveTab] = useState('composition'); - const [affectationSearchTerm, setAffectationSearchTerm] = useState(''); - const [selectedAffectationMaterielId, setSelectedAffectationMaterielId] = useState(null); + const [modalActiveTab, setModalActiveTab] = useState('composition'); // 'composition' | 'chaining' + const [pickerCategory, setPickerCategory] = useState('station'); + const [pickerSearchTerm, setPickerSearchTerm] = useState(''); + const [chainingSearchTerm, setChainingSearchTerm] = useState(''); + const [selectedChainingMaterielId, setSelectedChainingMaterielId] = useState(null); - const [loading, setLoading] = useState(true); + // Modale Couplage + const [couplingHoraire, setCouplingHoraire] = useState(null); + const [selectedCouplingCandidateId, setSelectedCouplingCandidateId] = useState(''); + const [isCouplingSaving, setIsCouplingSaving] = useState(false); + + // Modale Attribution Automatique & Planification + const [showAutoConfigModal, setShowAutoConfigModal] = useState(false); + const [isAutoAttributing, setIsAutoAttributing] = useState(false); + const [autoScheduleEnabled, setAutoScheduleEnabled] = useState(false); + const [autoScheduleTime, setAutoScheduleTime] = useState('04:00'); + const [lastAutoRunResult, setLastAutoRunResult] = useState(null); + const lastTriggeredMinuteRef = useRef(null); + + // Modale Optimisation Intelligente des Roulements + const [showOptimizationModal, setShowOptimizationModal] = useState(false); + const [isOptimizing, setIsOptimizing] = useState(false); + const [optimizationOptions, setOptimizationOptions] = useState({ + minTurnaroundMins: 15, + maxTurnaroundMins: 120, + optimalMinTurnaround: 15, + optimalMaxTurnaround: 45, + respectMaintenance: true, + balanceWorkload: true, + prioritizeHomeDepot: true, + resolveBreaks: true, + onlyUnassigned: false + }); + const [optimizationPreview, setOptimizationPreview] = useState(null); + + // Feedback & Sauvegarde const [saving, setSaving] = useState(false); const [error, setError] = useState(''); - const [saveMessage, setSaveMessage] = useState(''); + const [toastMessage, setToastMessage] = useState(null); + // Horaires enrichis avec la gestion des alternatives + const horairesWithAlternatives = useMemo(() => { + return groupHorairesWithAlternatives(horaires); + }, [horaires]); + + // Timelines précalculées pour toute la flotte + const timelines = useMemo(() => { + return buildMaterielTimeline(horaires, materiels); + }, [horaires, materiels]); + + // KPIs globaux de la flotte + const globalKpis = useMemo(() => { + return analyzeGlobalRoster(timelines, materiels, horaires); + }, [timelines, materiels, horaires]); + + // Chargement des données const loadData = useCallback(async () => { setLoading(true); setError(''); try { const regionId = getScopedRegionId(currentUser); - const [lignesRes, horairesRes, materielsRes] = await Promise.all([ + const [lignesRes, horairesRes, materielsRes, paramRes, depotsRes] = await Promise.all([ getAllLignes(regionId), getAllHoraires(regionId), - getAllMateriels() + getAllMateriels(), + getParametre('auto_attribution_config'), + getAllDepots() ]); if (lignesRes.error || horairesRes.error || materielsRes.error) { const firstError = lignesRes.error || horairesRes.error || materielsRes.error; - setError(firstError?.message || 'Erreur lors du chargement des données'); + setError(firstError?.message || 'Erreur lors du chargement des données ferroviaires'); } setLignes(Array.isArray(lignesRes.data) ? lignesRes.data : []); setHoraires(Array.isArray(horairesRes.data) ? horairesRes.data : []); setMateriels(Array.isArray(materielsRes.data) ? materielsRes.data : []); + setDepots(Array.isArray(depotsRes?.data) ? depotsRes.data : []); + setLastRefreshed(new Date()); + + // Configuration d'attribution automatique + if (paramRes?.data?.valeur) { + const cfg = paramRes.data.valeur; + if (cfg.enabled !== undefined) setAutoScheduleEnabled(!!cfg.enabled); + if (cfg.time) setAutoScheduleTime(cfg.time); + } } catch (err) { console.error('Erreur loadData attribution matériel roulant:', err); setError('Erreur lors du chargement des données'); @@ -133,19 +216,214 @@ export default function AttributionMaterielRoulant() { loadData(); }, [loadData]); + // Synchronisation des WCS Tabs + useEffect(() => { + const tabsEl = tabsRef.current; + if (tabsEl) { + const handleTabChange = (e) => { + const key = e.detail?.selectedKey || e.detail?.tabName; + if (key) setActiveTab(key); + }; + tabsEl.addEventListener('tabChange', handleTabChange); + return () => tabsEl.removeEventListener('tabChange', handleTabChange); + } + }, []); + + const showToast = (message, type = 'success') => { + setToastMessage({ message, type }); + setTimeout(() => { + setToastMessage(null); + }, 4500); + }; + + // Moteur d'attribution automatique + const executeAutoAttribution = async () => { + if (!canUpdate) return; + setIsAutoAttributing(true); + try { + const result = runAutoAttribution(horaires, materiels, { + minTurnaroundMins: 15, + maxTurnaroundMins: 90, + onlyUnassigned: true, + respectMaintenance: true + }); + + if (result.totalAssigned === 0) { + showToast('Toutes les circulations sont déjà pourvues ou aucun matériel compatible disponible.', 'info'); + setIsAutoAttributing(false); + return; + } + + // Appliquer les mises à jour de composition + for (const update of result.planUpdates) { + await updateHoraireComposition(update.horaireId, update.composition); + } + + // Appliquer les attributions automatiques de quai (règle des <= 45 min) + for (const quaiUp of result.quaiUpdates) { + await updateQuaisHoraire(quaiUp.horaireId, { depart: quaiUp.departQuai }); + } + + setLastAutoRunResult(result); + showToast( + `Attribution automatique terminée : ${result.totalAssigned} sillons pourvus (${result.totalQuaisPreserved} quais maintenus car escale ≤ 45 min).` + ); + await loadData(); + } catch (err) { + console.error('Erreur executeAutoAttribution:', err); + showToast("Erreur lors de l'exécution de l'attribution automatique", 'error'); + } finally { + setIsAutoAttributing(false); + } + }; + + // Sauvegarde de la configuration automatique + const handleSaveAutoConfig = async () => { + try { + await upsertParametre('auto_attribution_config', { + enabled: autoScheduleEnabled, + time: autoScheduleTime, + updated_at: new Date().toISOString() + }); + showToast(`Paramètres d'attribution automatique enregistrés (Heure : ${autoScheduleTime}).`); + setShowAutoConfigModal(false); + } catch (err) { + console.error('Erreur handleSaveAutoConfig:', err); + showToast('Erreur lors de la sauvegarde des paramètres', 'error'); + } + }; + + // Moteur d'Optimisation des Roulements + const openOptimizationModal = () => { + const result = optimizeMaterielRosters(horaires, materiels, optimizationOptions); + setOptimizationPreview(result); + setShowOptimizationModal(true); + }; + + const handleRunOptimizationSimulation = () => { + const result = optimizeMaterielRosters(horaires, materiels, optimizationOptions); + setOptimizationPreview(result); + showToast( + `Simulation recalculée : ${result.newAssignedCount} train(s) optimisé(s), ${result.quaisPreservedCount} quai(s) préservé(s).`, + 'info' + ); + }; + + const handleApplyOptimization = async () => { + if (!canUpdate || !optimizationPreview) return; + setIsOptimizing(true); + try { + const { planUpdates, quaiUpdates } = optimizationPreview; + + // 1. Sauvegarde des compositions optimisées par lots + if (planUpdates.length > 0) { + await updateHorairesCompositionsBatch(planUpdates); + } + + // 2. Sauvegarde des quais optimisés par lots + if (quaiUpdates.length > 0) { + const quaiBatchPayloads = quaiUpdates.map((q) => ({ + horaireId: q.horaireId, + payload: { depart: q.departQuai } + })); + await updateQuaisBatch(quaiBatchPayloads); + } + + showToast( + `Optimisation appliquée : ${planUpdates.length} roulement(s) optimisé(s), ${quaiUpdates.length} quai(s) harmonisé(s).`, + 'success' + ); + setShowOptimizationModal(false); + setOptimizationPreview(null); + await loadData(); + } catch (err) { + console.error('Erreur handleApplyOptimization:', err); + showToast("Erreur lors de l'application de l'optimisation", 'error'); + } finally { + setIsOptimizing(false); + } + }; + + // Timer de planification quotidienne à heure fixe + useEffect(() => { + if (!autoScheduleEnabled || !autoScheduleTime) return; + + const timer = setInterval(() => { + const now = new Date(); + const currentHours = String(now.getHours()).padStart(2, '0'); + const currentMinutes = String(now.getMinutes()).padStart(2, '0'); + const currentTimeString = `${currentHours}:${currentMinutes}`; + + if (currentTimeString === autoScheduleTime && lastTriggeredMinuteRef.current !== currentTimeString) { + lastTriggeredMinuteRef.current = currentTimeString; + console.log(`[AttributionMateriel] Déclenchement automatique planifié à ${currentTimeString}`); + executeAutoAttribution(); + } + }, 25000); + + return () => clearInterval(timer); + }, [autoScheduleEnabled, autoScheduleTime]); + + // Ligne sélectionnée const selectedLigne = useMemo( () => lignes.find((l) => String(l?.id) === String(selectedLigneId)) || null, [lignes, selectedLigneId] ); - const horairesLigne = useMemo(() => { - if (!selectedLigneId) return []; - return (horaires || []) - .filter((h) => String(h?.ligne_id || h?.ligne || '') === String(selectedLigneId)) - .filter((h) => matchesDirection(h, selectedLigne, directionFilter)) - .sort(sortByHoraire); - }, [horaires, selectedLigneId, selectedLigne, directionFilter]); + // Horaires filtrés avec gestion des alternatives + const filteredHoraires = useMemo(() => { + return (horairesWithAlternatives || []) + .filter((h) => { + if (selectedLigneId && String(h?.ligne_id || h?.ligne || '') !== String(selectedLigneId)) { + return false; + } + if (selectedLigne && !matchesDirection(h, selectedLigne, directionFilter)) { + return false; + } + + if (searchHoraireTerm) { + const q = searchHoraireTerm.toLowerCase(); + const matchNum = String(h?.numero_train || '').toLowerCase().includes(q); + const matchDep = String(h?.gare_depart_nom || '').toLowerCase().includes(q); + const matchArr = String(h?.gare_arrivee_nom || '').toLowerCase().includes(q); + const matchLigne = String(h?.ligne_nom || '').toLowerCase().includes(q); + if (!matchNum && !matchDep && !matchArr && !matchLigne) return false; + } + + const comp = Array.isArray(h?.composition_train) ? h.composition_train : []; + const hasComp = comp.length > 0; + const isRoad = isRoadService(h); + + if (assignmentFilter === 'unassigned') { + // Les cars/autocars ne sont pas des trains sans rame, ce sont des services routiers + if (isRoad || hasComp) return false; + } + if (assignmentFilter === 'assigned' && (!hasComp || isRoad)) return false; + if (assignmentFilter === 'road_services' && !isRoad) return false; + if (assignmentFilter === 'alternatives' && !h.isAlternative) return false; + if (assignmentFilter === 'anomaly') { + if (isRoad) return false; + const analysis = analyzeHoraireComposition(h, timelines, materiels); + if (!analysis.hasAnomalies) return false; + } + + return true; + }) + .sort(sortByHoraire); + }, [horairesWithAlternatives, selectedLigneId, selectedLigne, directionFilter, searchHoraireTerm, assignmentFilter, timelines, materiels]); + + // Analyse de la composition du train actuellement sélectionné dans la modale + const currentHoraireAnalysis = useMemo(() => { + if (!selectedHoraire) return null; + return analyzeHoraireComposition( + { ...selectedHoraire, composition_train: compositionDraft }, + timelines, + materiels + ); + }, [selectedHoraire, compositionDraft, timelines, materiels]); + + // Disponibilité des matériels pour le départ du train sélectionné const { materielsPresentsEnGare, materielsAuDepot, materielsAilleurs, materielsEnCirculation } = useMemo(() => { const allMateriels = Array.isArray(materiels) ? materiels : []; if (!selectedHoraire) { @@ -158,34 +436,30 @@ export default function AttributionMaterielRoulant() { } const departId = selectedHoraire?.gare_depart_id != null ? String(selectedHoraire.gare_depart_id) : null; - const departName = normalizeStationName(selectedHoraire?.gare_depart_nom); + const departName = normalizeStation(selectedHoraire?.gare_depart_nom); const targetTimeMins = timeToMinutes(selectedHoraire?.heure_depart); - const timelines = buildMaterielTimeline(horaires || [], allMateriels); - const presents = []; const depot = []; const ailleurs = []; const enCirculation = []; - allMateriels.forEach((materiel) => { - // Is this materiel already assigned to the selectedHoraire? - const isAlreadyAssigned = (selectedHoraire.composition_train || []).some(comp => String(comp.materiel_id) === String(materiel.id)); - if (isAlreadyAssigned) return; // Hide it from available lists since it's already in the draft/train + const alreadyDraftedIds = new Set(compositionDraft.map(c => String(c.materiel_id))); - const { status } = getMaterielStatusAtTime(timelines, materiel.id, targetTimeMins, departId, departName); - - // Additional check: is the materiel already assigned to another train at the exact same time or overlapping? - // Actually `getMaterielStatusAtTime` could check overlapping, but `timelines` is just a sequence. - // Let's rely on status. - if (status === 'PRESENT_EN_GARE') { - presents.push(materiel); - } else if (status === 'DEPOT') { - depot.push(materiel); - } else if (status === 'EN_CIRCULATION') { - enCirculation.push(materiel); + allMateriels.forEach((materiel) => { + if (alreadyDraftedIds.has(String(materiel.id))) return; + + const statusObj = getMaterielStatusAtTime(timelines, materiel.id, targetTimeMins, departId, departName); + const enrichedMat = { ...materiel, statusInfo: statusObj }; + + if (statusObj.status === 'PRESENT_EN_GARE') { + presents.push(enrichedMat); + } else if (statusObj.status === 'DEPOT') { + depot.push(enrichedMat); + } else if (statusObj.status === 'EN_CIRCULATION') { + enCirculation.push(enrichedMat); } else { - ailleurs.push(materiel); + ailleurs.push(enrichedMat); } }); @@ -195,21 +469,26 @@ export default function AttributionMaterielRoulant() { materielsAilleurs: ailleurs, materielsEnCirculation: enCirculation }; - }, [selectedHoraire, horaires, materiels]); + }, [selectedHoraire, compositionDraft, timelines, materiels]); const openCompositionModal = (horaire) => { if (!horaire) return; - setSaveMessage(''); + if (isRoadService(horaire)) { + showToast('Cette circulation est un service routier (Car / Autocar). Aucun matériel ferroviaire ne doit y être attribué.', 'info'); + return; + } setSelectedHoraire(horaire); - setCompositionDraft(toDraftComposition(horaire.composition_train)); + setCompositionDraft(toDraftComposition(horaire.composition_train, materiels)); setModalActiveTab('composition'); - setAffectationSearchTerm(''); + setPickerCategory('station'); + setPickerSearchTerm(''); + setChainingSearchTerm(''); + setSelectedChainingMaterielId(null); }; const closeCompositionModal = () => { setSelectedHoraire(null); setCompositionDraft([]); - setSaveMessage(''); }; const addMaterielToDraft = (materiel) => { @@ -222,6 +501,8 @@ export default function AttributionMaterielRoulant() { materiel_nom: materiel.nom || '', materiel_nom_technique: materiel.nom_technique || '', materiel_image_url: materiel.image_url || null, + capacite: materiel.capacite ? Number(materiel.capacite) : 0, + type_train: materiel.type_train || '', position: prev.length + 1 } ]); @@ -229,9 +510,10 @@ export default function AttributionMaterielRoulant() { const removeFromDraft = (localId) => { if (!canUpdate) return; - setCompositionDraft((prev) => prev - .filter((item) => item.local_id !== localId) - .map((item, idx) => ({ ...item, position: idx + 1 })) + setCompositionDraft((prev) => + prev + .filter((item) => item.local_id !== localId) + .map((item, idx) => ({ ...item, position: idx + 1 })) ); }; @@ -249,528 +531,1744 @@ export default function AttributionMaterielRoulant() { }); }; - const assignToNextHoraire = async (targetHoraire, materielId) => { - if (!canUpdate) return; - setSaving(true); - setSaveMessage(''); - try { - // Find the materiel full object to copy its properties - const materielObj = materiels.find(m => String(m.id) === String(materielId)); - if (!materielObj) throw new Error("Matériel introuvable"); - - const currentComp = Array.isArray(targetHoraire.composition_train) ? targetHoraire.composition_train : []; - - const newPayload = [...currentComp, { - materiel_id: materielObj.id, - materiel_nom: materielObj.nom || '', - materiel_nom_technique: materielObj.nom_technique || '', - materiel_image_url: materielObj.image_url || null, - position: currentComp.length + 1 - }]; - - const { error } = await updateHoraireComposition(targetHoraire.id, newPayload); - if (error) { - setSaveMessage(error.message || "Erreur lors de l'affectation au prochain train"); - } else { - setSaveMessage(`Le matériel a été affecté au train ${targetHoraire.numero_train}.`); - // Refresh data to reflect changes - await loadData(); - } - } catch (err) { - console.error("Erreur assignToNextHoraire:", err); - setSaveMessage("Erreur inattendue"); - } finally { - setSaving(false); - } - }; - + // Sauvegarder la composition du train avec la règle des quais (<= 45 min) const saveComposition = async () => { if (!selectedHoraire || !canUpdate) return; setSaving(true); - setSaveMessage(''); try { const payload = compositionDraft.map((item, idx) => ({ materiel_id: item.materiel_id, materiel_nom: item.materiel_nom || '', materiel_nom_technique: item.materiel_nom_technique || '', materiel_image_url: item.materiel_image_url || null, + capacite: item.capacite != null ? Number(item.capacite) : null, + type_train: item.type_train || '', position: idx + 1 })); const { data, error: saveError } = await updateHoraireComposition(selectedHoraire.id, payload); if (saveError) { - setSaveMessage(saveError.message || 'Erreur lors de la sauvegarde'); + showToast(saveError.message || 'Erreur lors de la sauvegarde de la composition', 'error'); setSaving(false); return; } - setHoraires((prev) => prev.map((h) => { - if (String(h?.id) !== String(selectedHoraire.id)) return h; - return { - ...h, - composition_train: data?.composition_train || payload - }; - })); - setSaveMessage('Composition enregistrée.'); + // Application de la règle des quais : si la rame provient d'un train A arrivant <= 45 min avant + if (compositionDraft.length > 0) { + const firstMatId = compositionDraft[0].materiel_id; + const matTimeline = timelines[firstMatId] || []; + const departM = timeToMinutes(selectedHoraire.heure_depart); + + const prevEvent = matTimeline + .filter(ev => String(ev.horaire_id) !== String(selectedHoraire.id) && ev.endMins <= departM) + .sort((a, b) => b.endMins - a.endMins)[0]; + + if (prevEvent) { + const quaiEval = evaluateQuaiContinuity(prevEvent.horaire, selectedHoraire); + if (quaiEval && quaiEval.shouldKeepSameQuai && quaiEval.arrivalQuai) { + await updateQuaisHoraire(selectedHoraire.id, { depart: quaiEval.arrivalQuai }); + showToast( + `Quai ${quaiEval.arrivalQuai} automatiquement attribué au train ${selectedHoraire.numero_train} (Escale ${quaiEval.deltaMins} min ≤ 45 min).` + ); + } + } + } + + setHoraires((prev) => + prev.map((h) => { + if (String(h?.id) !== String(selectedHoraire.id)) return h; + return { + ...h, + composition_train: data?.composition_train || payload + }; + }) + ); + + showToast(`Composition du train ${selectedHoraire.numero_train} enregistrée avec succès.`); closeCompositionModal(); } catch (err) { console.error('Erreur saveComposition:', err); - setSaveMessage('Erreur inattendue lors de la sauvegarde'); + showToast('Erreur inattendue lors de la sauvegarde', 'error'); } finally { setSaving(false); } }; - return ( -
Exploitation
-- Choisissez une ligne puis attribuez la composition des trains depuis la frise chronologique. -
-+ Système unifié d'affectation automatique et manuelle, préservation automatique des voies de quai, couplage de circulations et supervision du roulement. +
+Selectionnez une ligne pour afficher la frise chronologique.
-Chargement des horaires...
-Aucun horaire pour cette ligne.
-Vous avez un acces en lecture seule.
} - {!!saveMessage && selectedHoraire &&{saveMessage}
} + {/* Recherche de sillon */} +Chargement des horaires et des attributions matérielles…
+Modifiez vos filtres ou sélectionnez une autre ligne pour afficher les horaires.
+Aucun matériel roulant ajouté à ce train.
+ Sélectionnez une rame disponible dans le panneau de droite. +Aucun matériel dans cette catégorie pour cette heure.
+Ajoutez d'abord une rame dans l'onglet « Composition » pour lui planifier un enchaînement après son arrivée.
+Aucun train de correspondance trouvé au départ de {selectedHoraire.gare_arrivee_nom} après {formatHeure(selectedHoraire.heure_arrivee)}.
++ Exécute automatiquement l'algorithme d'attribution des rames à l'heure programmée chaque jour. +
++ Réassigne les sillons ayant des ruptures de gare ou des chevauchements d'horaires impossibles. +
++ Répartit équitablement le nombre de trajets et l'usure kilométrique sur toutes les rames disponibles. +
++ Favorise les enchaînements ramenant la rame vers son atelier d'attache en fin de journée pour la maintenance de nuit. +
++ Préserve l'intégralité des compositions existantes et comble uniquement les sillons vacants. +
+{materiel.nom_technique}
+Modifiez vos critères de recherche ou de filtre pour visualiser les roulements.
++ Gestion centralisée des technicentres, remisages, capacité des voies et ateliers de maintenance du réseau ferroviaire. +
+Votre base de données ne contient aucun dépôt de maintenance pour le moment.
+ {canUpdate && ( +Aucun train compatible au départ après cette arrivée.
+- Pilotage centralisé des circulations, gestion du plan de transport, détection des aléas et état des infrastructures en temps réel. -
-Toutes les circulations ferroviaires s'effectuent conformément au plan de transport.
-+ Supervision en temps réel des circulations, régulation des voies en gare, suivi de la ponctualité contractuelle et information voyageurs. +
+Commencez par créer des circulations ou importez une fiche horaire.
++ Objectif cible contractuel SNCF : 95.0% +
++ {totalHoraires - activePerturbations.length} trains conformes à l'horaire +
++ {countRetards} retard(s) · {countSuppressions} suppression(s) +
++ Impact voies et bus de substitution +
++ {uniqueLignes.size} lignes · {materiels.length} rames affectées +
+Ajoutez des horaires ou importez un plan de transport régional.
+Le trafic est entièrement régulier sans retard majeur signalé sur le réseau.
+{p.cause_details}
+ )} +Modifiez vos critères de recherche ou réinitialisez les filtres.
| N° Train | +Type | +Ligne Commerciale | +Départ | +Arrivée | +Arrêts | +Voie | +Statut Régulation | +Actions | +
|---|---|---|---|---|---|---|---|---|
|
+
+
+ |
+
+ |
+ {h.ligne_nom || '—'} | ++ {formatHeure(h.heure_depart)} + {h.gare_depart_nom} + | ++ {formatHeure(h.heure_arrivee)} + {h.gare_arrivee_nom} + | +
+ |
+ + {h.quai_depart ? ( + Voie {h.quai_depart} + ) : ( + — + )} + | +
+ {isCancelled ? (
+ |
+
+
+
+
-
-
+
+
+ |
+
Toutes les circulations respectent les plages horaires contractuelles.
+| Train | +Type d'aléa | +Trajet Affecté | +Impact / Retard | +Cause Déclarée | +État | +Actions | +
|---|---|---|---|---|---|---|
|
+
+
+ |
+
+ |
+ + {p.horaire?.gare_depart_nom || 'Départ'} → {p.horaire?.gare_arrivee_nom || 'Arrivée'} + | ++ {p.type_perturbation === 'retard' ? ( + +{p.duree_retard_minutes || 0} min + ) : p.type_perturbation === 'suppression' ? ( + Circulation Annulée + ) : ( + Itinéraire Adapté + )} + | ++ {p.cause || 'Motif non spécifié'} + | +
+ |
+
+ |
+
Aucune interruption de voie ni bus de substitution n'est actuellement déclaré.
++ {t.description || 'Travaux de modernisation et renouvellement d\'infrastructure des voies ferrées.'} +
+ +Système opérationnel & données à jour
+ +Configurez le parc de rames TER et TGV dans le module dédié.
++ {selectedItem.data.cause || 'Aucun motif d\'exploitation renseigné dans le système.'} +
+Recherchez et sélectionnez les horaires qui seront perturbés
+Recherchez et sélectionnez le ou les trains impactés par l'incident.
Configurez les perturbations pour les horaires sélectionnés
+Définissez la cause de l'incident, les retards par arrêt et les éventuelles substitutions.
Cliquez sur un train pour configurer les perturbations de ses arrêts
- - {selectedHoraires.map(h => ( -Affectation en cours...
+- Sillons utilisant le même matériel dans les 2 prochaines heures -
- {(() => { - const impacted = getImpactedHoraires(selectedHoraires[0]); - if (impacted.length === 0) { - returnAjoutez un ou plusieurs jours ou périodes d'application de la perturbation.
-- Configurez le motif commun et ajustez les arrêts de chaque train via les accordéons. -
-+ Configurez le motif commun et ajustez les arrêts de chaque train via les accordéons. +
+Horaire associé à cette perturbation (non modifiable)
-Chargement des données de la perturbation...
Configurez les perturbations pour chaque arrêt du parcours
-Cliquez sur une gare pour configurer ses perturbations
-- Sillons utilisant le même matériel dans les 2 prochaines heures -
- {(() => { - const impacted = getImpactedHoraires(); - if (impacted.length === 0) { - returnAjoutez un ou plusieurs jours ou périodes d'application de la perturbation.
-Chargement de la perturbation...
-Mettez à jour les retards, motifs, arrêts impactés et composition.
++ {selectedHoraire?.gare_depart_nom} ({formatHeure(selectedHoraire?.heure_depart)}) → {selectedHoraire?.gare_arrivee_nom} ({formatHeure(selectedHoraire?.heure_arrivee)}) +
Affectation en cours...
+Planifiez une coupure, des restrictions de circulation et les substitutions associées.
Définissez l'intitulé, la ligne concernée et les consignes d'exploitation.
-Période de validité du chantier, jours impactés et créneaux journaliers.
-Associez des cars ou trains spéciaux pour remplacer les circulations supprimées.
-Définissez l'intitulé, la ligne concernée et les consignes d'exploitation.
+Période de validité du chantier, jours impactés et créneaux journaliers.
+Associez des cars ou trains spéciaux pour remplacer les circulations supprimées.
+Recherche des circulations en cours...
-Aucun horaire de substitution trouvé pour « {searchQuery} ».
-Recherche des circulations en cours...
+Aucun horaire de substitution trouvé pour « {searchQuery} ».
+Chargement des données du chantier...
+Chargement des données du chantier...
Mettez à jour les caractéristiques, le calendrier et les substitutions associées.
+Ajustez les dates, les contraintes de voie et le plan de substitution associé.
Définissez l'intitulé, la ligne concernée et les consignes d'exploitation.
-Période de validité du chantier, jours impactés et créneaux journaliers.
-Associez des cars ou trains spéciaux pour remplacer les circulations supprimées.
-Définissez l'intitulé, la ligne concernée et les consignes d'exploitation.
+Période de validité du chantier, jours impactés et créneaux journaliers.
+Associez des cars ou trains spéciaux pour remplacer les circulations supprimées.
+Recherche des circulations en cours...
-Aucun horaire de substitution trouvé pour « {searchQuery} ».
-Recherche des circulations en cours...
+Aucun horaire de substitution trouvé pour « {searchQuery} ».
+