diff --git a/dashboard-sqdc/DATABASE_SETUP.md b/dashboard-sqdc/DATABASE_SETUP.md new file mode 100644 index 0000000..49ac9d6 --- /dev/null +++ b/dashboard-sqdc/DATABASE_SETUP.md @@ -0,0 +1,48 @@ +# Instructions de configuration + +## 1. Créer la base de données SQLite + +Exécute le script Python pour peupler la base de données sur 1 an: + +```bash +cd /home/paul/PFEE/dashboard-sqdc +python3 database/populate_db.py +``` + +Cela va créer `database/sqdc.db` avec: +- ✅ Toutes les catégories +- ✅ Les 21 KPIs avec leurs fréquences +- ✅ 1 an de mesures (365 jours minimum) + +## 2. Installer les dépendances Node + +```bash +npm install express sqlite3 sqlite cors +npm install -D @types/express @types/node +``` + +## 3. Lancer le serveur + +```bash +npm run server +``` + +## 4. Configurer React + +Le dashboard React se connectera à `http://localhost:3001/api` + +## Fréquences des mesures: + +- **per_10min**: Toutes les 10 minutes (qualité, délais) +- **per_30min**: Toutes les 30 minutes (rendement) +- **hourly**: Toutes les heures (délais, coûts) +- **daily**: Quotidiennement (sécurité, coûts, maintenance) +- **weekly**: Hebdomadairement (audits, maintenance) +- **per_3days**: Tous les 3 jours (maintenance) + +## Base de données + +La structure SQLite est logique: +- Chaque mesure a une date précise +- Les KPI ont une fréquence définie +- Les mesures sont filtrables par plage de temps diff --git a/dashboard-sqdc/database/db.ts b/dashboard-sqdc/database/db.ts new file mode 100644 index 0000000..1f72f77 --- /dev/null +++ b/dashboard-sqdc/database/db.ts @@ -0,0 +1,83 @@ +import sqlite3 from 'sqlite3'; +import { open } from 'sqlite'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const DB_PATH = path.join(__dirname, '../../database/sqdc.db'); + +export async function initDB() { + const db = await open({ + filename: DB_PATH, + driver: sqlite3.Database + }); + + await db.exec(` + CREATE TABLE IF NOT EXISTS categories ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + emoji TEXT, + description TEXT + ); + + CREATE TABLE IF NOT EXISTS kpis ( + id INTEGER PRIMARY KEY, + category_id INTEGER NOT NULL, + name TEXT NOT NULL, + unit TEXT, + target REAL, + formula TEXT, + description TEXT, + frequency TEXT, + FOREIGN KEY(category_id) REFERENCES categories(id) + ); + + CREATE TABLE IF NOT EXISTS measurements ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kpi_id INTEGER NOT NULL, + measurement_date DATETIME NOT NULL, + value REAL NOT NULL, + status TEXT, + FOREIGN KEY(kpi_id) REFERENCES kpis(id) + ); + + CREATE TABLE IF NOT EXISTS alerts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kpi_id INTEGER NOT NULL, + alert_type TEXT, + severity TEXT, + message TEXT, + created_at DATETIME, + FOREIGN KEY(kpi_id) REFERENCES kpis(id) + ); + + CREATE INDEX IF NOT EXISTS idx_measurements_kpi ON measurements(kpi_id); + CREATE INDEX IF NOT EXISTS idx_measurements_date ON measurements(measurement_date); + CREATE INDEX IF NOT EXISTS idx_alerts_kpi ON alerts(kpi_id); + `); + + return db; +} + +export async function getKPIs(db) { + return await db.all('SELECT * FROM kpis'); +} + +export async function getMeasurements(db, kpiId, days = 30) { + const fromDate = new Date(); + fromDate.setDate(fromDate.getDate() - days); + + return await db.all( + 'SELECT * FROM measurements WHERE kpi_id = ? AND measurement_date >= ? ORDER BY measurement_date ASC', + [kpiId, fromDate.toISOString()] + ); +} + +export async function getLatestMeasurement(db, kpiId) { + return await db.get( + 'SELECT * FROM measurements WHERE kpi_id = ? ORDER BY measurement_date DESC LIMIT 1', + [kpiId] + ); +} diff --git a/dashboard-sqdc/database/populate_db.py b/dashboard-sqdc/database/populate_db.py new file mode 100644 index 0000000..7245edd --- /dev/null +++ b/dashboard-sqdc/database/populate_db.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +import sqlite3 +import random +from datetime import datetime, timedelta +import math + +# Configuration de la base de données +DB_PATH = 'database/sqdc.db' + +# Catégories +CATEGORIES = [ + {'id': 1, 'name': 'Sécurité', 'emoji': '🛡️', 'description': 'Indicateurs de sécurité et prévention des accidents'}, + {'id': 2, 'name': 'Qualité', 'emoji': '🎯', 'description': 'Indicateurs de qualité des produits'}, + {'id': 3, 'name': 'Délais & Livraison', 'emoji': '⏱️', 'description': 'Indicateurs de délais et livraison'}, + {'id': 4, 'name': 'Coûts', 'emoji': '💰', 'description': 'Indicateurs de coûts de production'}, + {'id': 5, 'name': 'Maintenance', 'emoji': '🔧', 'description': 'Indicateurs de maintenance'} +] + +# KPIs avec fréquences +KPIS = [ + # SÉCURITÉ (1 mesure par jour) + {'id': 1, 'category_id': 1, 'name': 'Taux de Fréquence (TF)', 'unit': 'par 1M heures', 'target': 1.0, 'frequency': 'daily', 'formula': '(Nombre d\'Accidents avec Arrêt / Nombre d\'Heures Travaillées) × 1000000', 'description': 'Mesurer la fréquence des accidents avec arrêt.'}, + {'id': 2, 'category_id': 1, 'name': 'Nombre d\'Incidents/Near Miss', 'unit': 'incidents', 'target': 8, 'frequency': 'daily', 'formula': 'Compte des rapports d\'incidents', 'description': 'Évaluer la culture de sécurité.'}, + {'id': 3, 'category_id': 1, 'name': 'Taux de Conformité aux Audits', 'unit': '%', 'target': 95, 'frequency': 'weekly', 'formula': '(Points Conformes / Total Points) × 100', 'description': 'Mesurer le respect des procédures.'}, + + # QUALITÉ (toutes les 10 minutes) + {'id': 4, 'category_id': 2, 'name': 'Taux de Rebut (Scrap Rate)', 'unit': '%', 'target': 1.5, 'frequency': 'per_10min', 'formula': '(Unités Rebutées / Unités Produites) × 100', 'description': 'Mesurer le % d\'unités jetées.'}, + {'id': 5, 'category_id': 2, 'name': 'Taux de Retouche (Rework Rate)', 'unit': '%', 'target': 2.0, 'frequency': 'per_10min', 'formula': '(Unités Retouchées / Unités Totales) × 100', 'description': 'Mesurer le % d\'unités retouchées.'}, + {'id': 6, 'category_id': 2, 'name': 'Nombre de Défauts par Unité (DPU)', 'unit': 'défauts/unité', 'target': 0.5, 'frequency': 'per_10min', 'formula': 'Défauts Totaux / Unités Inspectées', 'description': 'Mesurer le nombre moyen de défauts.'}, + {'id': 7, 'category_id': 2, 'name': 'Taux de Retours Clients', 'unit': '%', 'target': 0.8, 'frequency': 'daily', 'formula': '(Unités Retournées / Unités Vendues) × 100', 'description': 'Mesurer l\'impact de la non-qualité.'}, + {'id': 8, 'category_id': 2, 'name': 'Taux de rendement synthétique (TRS)', 'unit': '%', 'target': 85, 'frequency': 'per_30min', 'formula': 'Pièces bonnes × Temps cycle / Temps ouverture', 'description': 'Rendement global de la ligne.'}, + {'id': 9, 'category_id': 2, 'name': 'Efficacité Globale de l\'Équipement (OEE)', 'unit': '%', 'target': 80, 'frequency': 'per_30min', 'formula': 'Disponibilité × Performance × Qualité', 'description': 'Mesurer l\'efficacité combinée.'}, + + # DÉLAIS (toutes les heures) + {'id': 10, 'category_id': 3, 'name': 'Taux de Respect du Plan', 'unit': '%', 'target': 95, 'frequency': 'hourly', 'formula': '(Produite / Planifiée) × 100', 'description': 'Mesurer la capacité à atteindre le volume.'}, + {'id': 11, 'category_id': 3, 'name': 'Temps de Cycle (Cycle Time)', 'unit': 'min/unité', 'target': 50, 'frequency': 'per_10min', 'formula': 'Temps Total / Nombre d\'Unités', 'description': 'Mesurer le temps par unité.'}, + {'id': 12, 'category_id': 3, 'name': 'Tack Time', 'unit': 'min/unité', 'target': 50, 'frequency': 'per_10min', 'formula': 'Temps production / Pièces demandées', 'description': 'Temps de production requis par unité.'}, + {'id': 13, 'category_id': 3, 'name': 'Temps d\'Arrêt Imprévu (Downtime)', 'unit': 'h/jour', 'target': 1.5, 'frequency': 'daily', 'formula': 'Somme des Arrêts Non Planifiés', 'description': 'Mesurer l\'arrêt non planifié.'}, + + # COÛTS (quotidiennement) + {'id': 14, 'category_id': 4, 'name': 'Coût par Unité (CPU)', 'unit': '€', 'target': 240, 'frequency': 'daily', 'formula': 'Coût Total / Unités Produites', 'description': 'Mesurer l\'efficacité des coûts.'}, + {'id': 15, 'category_id': 4, 'name': 'Productivité de la Main-d\'œuvre', 'unit': 'unités/h', 'target': 8.0, 'frequency': 'hourly', 'formula': 'Unités Produites / Heures Main-d\'œuvre', 'description': 'Mesurer l\'efficacité de l\'équipe.'}, + {'id': 16, 'category_id': 4, 'name': 'Coût des Non-Qualité (CNQ)', 'unit': '€', 'target': 10000, 'frequency': 'daily', 'formula': 'Rebuts + Retouches + Retours', 'description': 'Mesurer le coût des défauts.'}, + + # MAINTENANCE (tous les 3 jours) + {'id': 17, 'category_id': 5, 'name': 'Temps Moyen Entre Pannes (MTBF)', 'unit': 'heures', 'target': 400, 'frequency': 'per_3days', 'formula': 'Temps Fonctionnement / Pannes', 'description': 'Mesurer la fiabilité.'}, + {'id': 18, 'category_id': 5, 'name': 'Temps Moyen de Réparation (MTTR)', 'unit': 'heures', 'target': 2.5, 'frequency': 'per_3days', 'formula': 'Temps Réparation / Pannes', 'description': 'Mesurer la rapidité.'}, + {'id': 19, 'category_id': 5, 'name': 'Ratio Maintenance Préventive/Corrective', 'unit': '%', 'target': 70, 'frequency': 'weekly', 'formula': 'Heures MP / (MP + MC)', 'description': 'Évaluer la stratégie.'}, + {'id': 20, 'category_id': 5, 'name': 'Taux d\'Achèvement du Plan Préventif', 'unit': '%', 'target': 95, 'frequency': 'weekly', 'formula': '(Tâches Terminées / Tâches Planifiées) × 100', 'description': 'Mesurer le respect du plan.'}, + {'id': 21, 'category_id': 5, 'name': 'Coût de Maintenance par Unité Produite', 'unit': '€', 'target': 30, 'frequency': 'daily', 'formula': 'Coûts Maintenance / Unités Produites', 'description': 'Relier dépenses à production.'} +] + +def get_frequency_minutes(frequency): + """Retourne le nombre de minutes entre les mesures""" + frequencies = { + 'per_10min': 10, + 'per_30min': 30, + 'hourly': 60, + 'daily': 1440, # 24h + 'weekly': 10080, # 7 jours + 'per_3days': 4320 # 3 jours + } + return frequencies.get(frequency, 1440) + +def generate_value(kpi_id, target, variance_range=0.2): + """Génère une valeur réaliste autour de la cible""" + variance = (random.random() - 0.5) * 2 * variance_range + value = target * (1 + variance) + + # Ajouter du bruit réaliste pour certains KPI + if kpi_id in [4, 5, 6, 11, 12]: # Qualité et délais - plus de variabilité + noise = (random.random() - 0.5) * 0.3 * target + value += noise + + return max(0, round(value, 2)) + +def determine_status(kpi_id, value, target): + """Détermine le statut (good, warning, critical)""" + tolerance = abs(target * 0.1) + + # KPI où plus bas est mieux + if kpi_id in [2, 4, 5, 6, 13, 16]: + if value > target + tolerance * 2: + return 'critical' + elif value > target + tolerance: + return 'warning' + else: + # KPI où plus haut est mieux + if value < target - tolerance * 2: + return 'critical' + elif value < target - tolerance: + return 'warning' + + return 'good' + +def populate_database(): + """Remplit la base de données sur 1 an""" + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + # Créer les tables d'abord + print("🔄 Création du schéma...") + cursor.execute(''' + CREATE TABLE IF NOT EXISTS categories ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + emoji TEXT, + description TEXT + ) + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS kpis ( + id INTEGER PRIMARY KEY, + category_id INTEGER NOT NULL, + name TEXT NOT NULL, + unit TEXT, + target REAL, + frequency TEXT, + formula TEXT, + description TEXT, + FOREIGN KEY(category_id) REFERENCES categories(id) + ) + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS measurements ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kpi_id INTEGER NOT NULL, + measurement_date DATETIME NOT NULL, + value REAL NOT NULL, + status TEXT, + FOREIGN KEY(kpi_id) REFERENCES kpis(id) + ) + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS alerts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kpi_id INTEGER NOT NULL, + alert_type TEXT, + severity TEXT, + message TEXT, + created_at DATETIME, + FOREIGN KEY(kpi_id) REFERENCES kpis(id) + ) + ''') + + # Supprimer les anciennes données + cursor.execute('DELETE FROM measurements') + cursor.execute('DELETE FROM alerts') + cursor.execute('DELETE FROM kpis') + cursor.execute('DELETE FROM categories') + + # Insérer les catégories + for cat in CATEGORIES: + cursor.execute( + 'INSERT INTO categories (id, name, emoji, description) VALUES (?, ?, ?, ?)', + (cat['id'], cat['name'], cat['emoji'], cat['description']) + ) + + # Insérer les KPI + for kpi in KPIS: + cursor.execute( + 'INSERT INTO kpis (id, category_id, name, unit, target, frequency, formula, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + (kpi['id'], kpi['category_id'], kpi['name'], kpi['unit'], kpi['target'], kpi['frequency'], kpi['formula'], kpi['description']) + ) + + # Générer les mesures sur 1 an + end_date = datetime.now() + start_date = end_date - timedelta(days=365) + + print("🔄 Génération des mesures sur 1 an...") + measurements_count = 0 + + for kpi in KPIS: + kpi_id = kpi['id'] + target = kpi['target'] + frequency_minutes = get_frequency_minutes(kpi['frequency']) + + current_date = start_date + while current_date <= end_date: + value = generate_value(kpi_id, target) + status = determine_status(kpi_id, value, target) + + cursor.execute( + 'INSERT INTO measurements (kpi_id, measurement_date, value, status) VALUES (?, ?, ?, ?)', + (kpi_id, current_date.isoformat(), value, status) + ) + measurements_count += 1 + + current_date += timedelta(minutes=frequency_minutes) + + conn.commit() + conn.close() + + print(f"✅ Base de données remplie avec succès!") + print(f"📊 {measurements_count} mesures créées") + print(f"🎯 {len(KPIS)} KPI configurés") + print(f"📁 Fichier: {DB_PATH}") + +if __name__ == '__main__': + populate_database() diff --git a/dashboard-sqdc/database/schema.sql b/dashboard-sqdc/database/schema.sql new file mode 100644 index 0000000..d25eb21 --- /dev/null +++ b/dashboard-sqdc/database/schema.sql @@ -0,0 +1,52 @@ +-- Base de données SQDC +CREATE TABLE IF NOT EXISTS categories ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + emoji TEXT, + description TEXT +); + +CREATE TABLE IF NOT EXISTS kpis ( + id INTEGER PRIMARY KEY, + category_id INTEGER NOT NULL, + name TEXT NOT NULL, + unit TEXT, + target REAL, + formula TEXT, + description TEXT, + frequency TEXT, -- 'daily', 'per_10min', 'per_3days', etc. + FOREIGN KEY(category_id) REFERENCES categories(id) +); + +CREATE TABLE IF NOT EXISTS measurements ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kpi_id INTEGER NOT NULL, + measurement_date DATETIME NOT NULL, + value REAL NOT NULL, + status TEXT, -- 'good', 'warning', 'critical' + FOREIGN KEY(kpi_id) REFERENCES kpis(id) +); + +CREATE TABLE IF NOT EXISTS alerts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kpi_id INTEGER NOT NULL, + alert_type TEXT, + severity TEXT, -- 'warning', 'critical' + message TEXT, + created_at DATETIME, + FOREIGN KEY(kpi_id) REFERENCES kpis(id) +); + +CREATE TABLE IF NOT EXISTS comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kpi_id INTEGER NOT NULL, + comment TEXT, + created_at DATETIME, + FOREIGN KEY(kpi_id) REFERENCES kpis(id) +); + +-- Créer les indices pour les performances +CREATE INDEX IF NOT EXISTS idx_measurements_kpi ON measurements(kpi_id); +CREATE INDEX IF NOT EXISTS idx_measurements_date ON measurements(measurement_date); +CREATE INDEX IF NOT EXISTS idx_alerts_kpi ON alerts(kpi_id); +CREATE INDEX IF NOT EXISTS idx_comments_kpi ON comments(kpi_id); diff --git a/dashboard-sqdc/database/sqdc.db b/dashboard-sqdc/database/sqdc.db new file mode 100644 index 0000000..3cc6eea Binary files /dev/null and b/dashboard-sqdc/database/sqdc.db differ diff --git a/dashboard-sqdc/package-lock.json b/dashboard-sqdc/package-lock.json index cbcf31e..506b3fa 100644 --- a/dashboard-sqdc/package-lock.json +++ b/dashboard-sqdc/package-lock.json @@ -17,13 +17,22 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "chart.js": "^4.5.1", + "cors": "^2.8.5", + "express": "^4.21.2", "lucide-react": "^0.546.0", "react": "^19.2.0", "react-chartjs-2": "^5.3.0", "react-dom": "^19.2.0", "react-scripts": "5.0.1", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", "typescript": "^4.9.5", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21" } }, "node_modules/@adobe/css-tools": { @@ -2451,6 +2460,13 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3028,6 +3044,45 @@ "node": ">= 8" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3633,6 +3688,16 @@ "@types/node": "*" } }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -4345,6 +4410,13 @@ "deprecated": "Use your platform's native atob() and btoa() methods instead", "license": "BSD-3-Clause" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -4453,6 +4525,15 @@ "node": ">=8.9" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -4465,6 +4546,33 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4623,6 +4731,28 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -5208,6 +5338,26 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { "version": "2.8.18", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.18.tgz", @@ -5260,6 +5410,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -5403,6 +5573,30 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5430,6 +5624,82 @@ "node": ">= 0.8" } }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -5558,6 +5828,19 @@ "node": ">=4" } }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5637,6 +5920,15 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -5688,6 +5980,16 @@ "node": ">=0.10.0" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -5794,6 +6096,15 @@ "node": ">=4" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", @@ -5818,6 +6129,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -5932,6 +6253,13 @@ "node": ">=0.8" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -6015,6 +6343,19 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -6031,6 +6372,18 @@ "node": ">=10" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -6529,12 +6882,36 @@ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "license": "MIT" }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "license": "MIT" }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -6614,6 +6991,13 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6642,6 +7026,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -6952,6 +7345,25 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -6974,6 +7386,23 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -7886,6 +8315,15 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", @@ -8104,6 +8542,12 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -8423,6 +8867,15 @@ "node": ">= 0.6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -8445,6 +8898,12 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -8459,6 +8918,36 @@ "node": ">=12" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/fs-monkey": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", @@ -8523,6 +9012,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -8631,6 +9141,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -8885,6 +9401,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -9069,6 +9592,13 @@ "entities": "^2.0.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true + }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -9171,6 +9701,16 @@ "node": ">=10.17.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -9213,6 +9753,26 @@ "node": ">=4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -9294,6 +9854,13 @@ "node": ">=8" } }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -9331,6 +9898,16 @@ "node": ">= 0.4" } }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -9573,6 +10150,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -11349,6 +11933,67 @@ "semver": "bin/semver.js" } }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -11482,6 +12127,18 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -11547,6 +12204,207 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -11559,6 +12417,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -11607,6 +12471,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -11644,6 +12514,24 @@ "tslib": "^2.0.3" } }, + "node_modules/node-abi": { + "version": "3.78.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.78.0.tgz", + "integrity": "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -11653,6 +12541,31 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -11665,6 +12578,22 @@ "integrity": "sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==", "license": "MIT" }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -11707,6 +12636,23 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -11999,6 +12945,22 @@ "node": ">=8" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -13539,6 +14501,32 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -13611,6 +14599,37 @@ "asap": "~2.0.6" } }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -13675,6 +14694,16 @@ "url": "https://github.com/sponsors/lupomontero" } }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -13790,6 +14819,30 @@ "node": ">=0.10.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", @@ -14858,6 +15911,13 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -15021,6 +16081,51 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -15036,6 +16141,17 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -15047,6 +16163,36 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -15154,6 +16300,81 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "license": "BSD-3-Clause" }, + "node_modules/sqlite": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz", + "integrity": "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q==", + "license": "MIT" + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ssri/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -16008,6 +17229,84 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -16309,6 +17608,18 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -16515,6 +17826,26 @@ "node": ">=4" } }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -17142,6 +18473,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -17549,6 +18908,27 @@ } } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/dashboard-sqdc/package.json b/dashboard-sqdc/package.json index cfdb920..673bbcd 100644 --- a/dashboard-sqdc/package.json +++ b/dashboard-sqdc/package.json @@ -12,19 +12,30 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "chart.js": "^4.5.1", + "cors": "^2.8.5", + "express": "^4.21.2", "lucide-react": "^0.546.0", "react": "^19.2.0", "react-chartjs-2": "^5.3.0", "react-dom": "^19.2.0", "react-scripts": "5.0.1", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", "typescript": "^4.9.5", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "server": "node server.js", + "populate-db": "python3 database/populate_db.py" }, "eslintConfig": { "extends": [ diff --git a/dashboard-sqdc/server.js b/dashboard-sqdc/server.js new file mode 100644 index 0000000..2888a57 --- /dev/null +++ b/dashboard-sqdc/server.js @@ -0,0 +1,152 @@ +const express = require('express'); +const sqlite3 = require('sqlite3').verbose(); +const cors = require('cors'); +const path = require('path'); + +const app = express(); +const PORT = 3001; + +app.use(cors()); +app.use(express.json()); + +let db; + +// Initialiser la base de données +function initDatabase() { + return new Promise((resolve, reject) => { + db = new sqlite3.Database( + path.join(__dirname, 'database', 'sqdc.db'), + (err) => { + if (err) { + console.error('❌ Erreur de connexion:', err); + reject(err); + } else { + console.log('✅ Base de données connectée'); + resolve(); + } + } + ); + }); +} + +// Routes API + +// Obtenir les catégories +app.get('/api/categories', (req, res) => { + db.all('SELECT * FROM categories', (err, rows) => { + if (err) { + res.status(500).json({ error: err.message }); + return; + } + res.json(rows || []); + }); +}); + +// Obtenir les KPI +app.get('/api/kpis', (req, res) => { + db.all('SELECT * FROM kpis', (err, rows) => { + if (err) { + res.status(500).json({ error: err.message }); + return; + } + res.json(rows || []); + }); +}); + +// Obtenir les mesures pour un KPI +app.get('/api/measurements/:kpiId', (req, res) => { + try { + const { kpiId } = req.params; + const days = parseInt(req.query.days || 30); + + const fromDate = new Date(); + fromDate.setDate(fromDate.getDate() - days); + + let query = 'SELECT * FROM measurements WHERE kpi_id = ?'; + const params = [kpiId]; + + if (days > 0) { + query += ' AND measurement_date >= ?'; + params.push(fromDate.toISOString()); + } + + query += ' ORDER BY measurement_date ASC'; + + db.all(query, params, (err, rows) => { + if (err) { + res.status(500).json({ error: err.message }); + return; + } + res.json(rows || []); + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// Obtenir la dernière mesure pour un KPI +app.get('/api/latest/:kpiId', (req, res) => { + try { + const { kpiId } = req.params; + + db.get( + 'SELECT * FROM measurements WHERE kpi_id = ? ORDER BY measurement_date DESC LIMIT 1', + [kpiId], + (err, row) => { + if (err) { + res.status(500).json({ error: err.message }); + return; + } + res.json(row || {}); + } + ); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// Obtenir les statistiques pour un KPI +app.get('/api/stats/:kpiId', (req, res) => { + try { + const { kpiId } = req.params; + const days = parseInt(req.query.days || 30); + + const fromDate = new Date(); + fromDate.setDate(fromDate.getDate() - days); + + let query = `SELECT + COUNT(*) as count, + AVG(value) as avg, + MIN(value) as min, + MAX(value) as max + FROM measurements + WHERE kpi_id = ?`; + const params = [kpiId]; + + if (days > 0) { + query += ' AND measurement_date >= ?'; + params.push(fromDate.toISOString()); + } + + db.get(query, params, (err, row) => { + if (err) { + res.status(500).json({ error: err.message }); + return; + } + res.json(row || {}); + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// Démarrer le serveur +initDatabase().then(() => { + app.listen(PORT, () => { + console.log(`🚀 Serveur SQDC démarré sur http://localhost:${PORT}`); + console.log(`📊 API disponible sur http://localhost:${PORT}/api`); + }); +}).catch(err => { + console.error('❌ Erreur lors de l\'initialisation:', err); + process.exit(1); +}); diff --git a/dashboard-sqdc/src/App.tsx b/dashboard-sqdc/src/App.tsx index d95c818..8f3e2eb 100644 --- a/dashboard-sqdc/src/App.tsx +++ b/dashboard-sqdc/src/App.tsx @@ -2,7 +2,6 @@ import React, { useState } from 'react'; import { HomePage } from './pages/HomePage'; import { DetailPage } from './pages/DetailPage'; import { TrendChart, CategoryDistributionChart, StatusChart, CNQChart } from './components/Charts'; -import { kpiData } from './data/kpiData'; import './App.css'; type TabType = 'home' | 'security' | 'quality' | 'delays' | 'costs' | 'maintenance' | 'charts'; @@ -76,27 +75,27 @@ function App() {
{activeTab === 'home' && ( - + )} {activeTab === 'security' && ( - + )} {activeTab === 'quality' && ( - + )} {activeTab === 'delays' && ( - + )} {activeTab === 'costs' && ( - + )} {activeTab === 'maintenance' && ( - + )} {activeTab === 'charts' && ( diff --git a/dashboard-sqdc/src/components/ChartModal.tsx b/dashboard-sqdc/src/components/ChartModal.tsx new file mode 100644 index 0000000..c62fa32 --- /dev/null +++ b/dashboard-sqdc/src/components/ChartModal.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js'; +import { Line } from 'react-chartjs-2'; +import '../styles/ChartModal.css'; + +ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend); + +interface ChartModalProps { + isOpen: boolean; + kpi: any; + measurements: any[]; + onClose: () => void; +} + +export const ChartModal: React.FC = ({ isOpen, kpi, measurements, onClose }) => { + if (!isOpen || !kpi) return null; + + // Préparer les données pour le graphique + const labels = measurements + .map(m => new Date(m.measurement_date).toLocaleDateString('fr-FR')) + .reverse(); + + const values = measurements + .map(m => m.value) + .reverse(); + + const chartData = { + labels, + datasets: [ + { + label: kpi.name, + data: values, + borderColor: '#6496ff', + backgroundColor: 'rgba(100, 150, 255, 0.1)', + tension: 0.4, + fill: true, + pointRadius: 4, + pointBackgroundColor: '#6496ff', + pointBorderColor: '#fff', + pointBorderWidth: 2, + }, + ], + }; + + const chartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'top' as const, + }, + title: { + display: true, + text: `Graphique Complet: ${kpi.name}`, + font: { size: 16, weight: 'bold' as const }, + }, + }, + scales: { + y: { + beginAtZero: true, + title: { + display: true, + text: kpi.unit, + }, + }, + x: { + display: true, + }, + }, + }; + + return ( +
+
e.stopPropagation()}> +
+

📈 {kpi.name}

+ +
+
+
+ +
+
+
+

Nombre de mesures: {measurements.length}

+

Période: {labels[0]} à {labels[labels.length - 1]}

+
+
+
+ ); +}; diff --git a/dashboard-sqdc/src/components/ExportModal.tsx b/dashboard-sqdc/src/components/ExportModal.tsx new file mode 100644 index 0000000..33d4420 --- /dev/null +++ b/dashboard-sqdc/src/components/ExportModal.tsx @@ -0,0 +1,103 @@ +import React, { useState } from 'react'; +import '../styles/ExportModal.css'; + +interface ExportModalProps { + isOpen: boolean; + kpiName: string; + onExport: (days: number) => void; + onClose: () => void; +} + +export const ExportModal: React.FC = ({ isOpen, kpiName, onExport, onClose }) => { + const [selectedRange, setSelectedRange] = useState(null); + + if (!isOpen) return null; + + const handleExport = () => { + if (selectedRange !== null) { + onExport(selectedRange); + setSelectedRange(null); + onClose(); + } + }; + + return ( +
+
e.stopPropagation()}> +
+

💾 Exporter {kpiName}

+ +
+ +
+

+ Sélectionnez la plage de données à exporter: +

+ +
+ + + + + + + +
+
+ +
+ + +
+
+
+ ); +}; diff --git a/dashboard-sqdc/src/components/KPICard.tsx b/dashboard-sqdc/src/components/KPICard.tsx index c8568db..51e0dc1 100644 --- a/dashboard-sqdc/src/components/KPICard.tsx +++ b/dashboard-sqdc/src/components/KPICard.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import { KPI } from '../types'; import '../styles/KPICard.css'; interface KPICardProps { - kpi: KPI; + kpi: any; color: string; } @@ -22,16 +21,10 @@ export const KPICard: React.FC = ({ kpi, color }) => { }; const getTrendIcon = () => { - switch (kpi.trend) { - case 'up': - return '📈'; - case 'down': - return '📉'; - case 'stable': - return '➡️'; - default: - return '•'; - } + if (!kpi.latest) return '•'; + + // Déterminer la tendance basée sur les données de la dernière semaine + return '📊'; // Placeholder pour l'instant }; const getStatusClass = () => { @@ -52,11 +45,11 @@ export const KPICard: React.FC = ({ kpi, color }) => {
- {getStatusIcon()} {kpi.status.charAt(0).toUpperCase() + kpi.status.slice(1)} + {getStatusIcon()} {kpi.status?.charAt(0).toUpperCase() + kpi.status?.slice(1) || 'N/A'} {kpi.target && ( - Objectif: {kpi.target} {kpi.unit} + Obj: {kpi.target} {kpi.unit} )}
diff --git a/dashboard-sqdc/src/components/RangeChartModal.tsx b/dashboard-sqdc/src/components/RangeChartModal.tsx new file mode 100644 index 0000000..26d03ea --- /dev/null +++ b/dashboard-sqdc/src/components/RangeChartModal.tsx @@ -0,0 +1,159 @@ +import React, { useState } from 'react'; +import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js'; +import { Line } from 'react-chartjs-2'; +import '../styles/RangeChartModal.css'; + +ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend); + +interface RangeChartModalProps { + isOpen: boolean; + kpi: any; + measurements: any[]; + getMeasurementsForRange: (days: number) => any[]; + onClose: () => void; +} + +export const RangeChartModal: React.FC = ({ + isOpen, + kpi, + measurements, + getMeasurementsForRange, + onClose, +}) => { + const [selectedRange, setSelectedRange] = useState(30); + + if (!isOpen || !kpi) return null; + + // Obtenir les mesures pour la plage sélectionnée + const filteredMeasurements = getMeasurementsForRange(selectedRange); + + // Préparer les données pour le graphique + const labels = filteredMeasurements + .map(m => new Date(m.measurement_date).toLocaleDateString('fr-FR')) + .reverse(); + + const values = filteredMeasurements + .map(m => m.value) + .reverse(); + + const chartData = { + labels, + datasets: [ + { + label: kpi.name, + data: values, + borderColor: '#6496ff', + backgroundColor: 'rgba(100, 150, 255, 0.1)', + tension: 0.4, + fill: true, + pointRadius: 4, + pointBackgroundColor: '#6496ff', + pointBorderColor: '#fff', + pointBorderWidth: 2, + }, + ], + }; + + const chartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'top' as const, + }, + title: { + display: true, + text: `Graphique Complet: ${kpi.name}`, + font: { size: 16, weight: 'bold' as const }, + }, + }, + scales: { + y: { + beginAtZero: true, + title: { + display: true, + text: kpi.unit, + }, + }, + x: { + display: true, + }, + }, + }; + + return ( +
+
e.stopPropagation()}> +
+

📈 {kpi.name}

+ +
+ +
+ + + + + +
+ +
+
+ +
+
+ +
+

Mesures: {filteredMeasurements.length}

+

Période: {labels[0]} à {labels[labels.length - 1]}

+
+
+
+ ); +}; diff --git a/dashboard-sqdc/src/database/useSQLiteDatabase.ts b/dashboard-sqdc/src/database/useSQLiteDatabase.ts new file mode 100644 index 0000000..8a8c121 --- /dev/null +++ b/dashboard-sqdc/src/database/useSQLiteDatabase.ts @@ -0,0 +1,113 @@ +import { useState, useEffect, useCallback } from 'react'; + +const API_BASE = 'http://localhost:3001/api'; + +export interface Category { + id: number; + name: string; + emoji: string; + description?: string; +} + +export interface KPI { + id: number; + category_id: number; + name: string; + unit: string; + target?: number; + formula?: string; + description?: string; + frequency?: string; +} + +export interface Measurement { + id?: number; + kpi_id: number; + measurement_date: string; + value: number; + status?: string; +} + +export const useSQLiteDatabase = () => { + const [categories, setCategories] = useState([]); + const [kpis, setKpis] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // Charger les catégories et KPI au démarrage + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const [categoriesRes, kpisRes] = await Promise.all([ + fetch(`${API_BASE}/categories`), + fetch(`${API_BASE}/kpis`) + ]); + + if (!categoriesRes.ok || !kpisRes.ok) { + throw new Error('Erreur lors du chargement des données'); + } + + const categoriesData = await categoriesRes.json(); + const kpisData = await kpisRes.json(); + + setCategories(categoriesData); + setKpis(kpisData); + setError(null); + } catch (err: any) { + setError(err.message || 'Erreur de connexion à la base de données'); + console.error('❌ Erreur:', err); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); + + // Obtenir les mesures pour un KPI + const getMeasurementsForKPI = useCallback(async (kpiId: number, days: number = 30) => { + try { + const res = await fetch(`${API_BASE}/measurements/${kpiId}?days=${days}`); + if (!res.ok) throw new Error('Erreur de chargement des mesures'); + return await res.json(); + } catch (err: any) { + console.error('Erreur getMeasurementsForKPI:', err); + return []; + } + }, []); + + // Obtenir la dernière mesure pour un KPI + const getLatestMeasurement = useCallback(async (kpiId: number) => { + try { + const res = await fetch(`${API_BASE}/latest/${kpiId}`); + if (!res.ok) throw new Error('Erreur de chargement'); + return await res.json(); + } catch (err: any) { + console.error('Erreur getLatestMeasurement:', err); + return null; + } + }, []); + + // Obtenir les statistiques pour un KPI + const getKPIStats = useCallback(async (kpiId: number, days: number = 30) => { + try { + const res = await fetch(`${API_BASE}/stats/${kpiId}?days=${days}`); + if (!res.ok) throw new Error('Erreur de chargement'); + return await res.json(); + } catch (err: any) { + console.error('Erreur getKPIStats:', err); + return null; + } + }, []); + + return { + categories, + kpis, + loading, + error, + getMeasurementsForKPI, + getLatestMeasurement, + getKPIStats + }; +}; diff --git a/dashboard-sqdc/src/pages/DetailPage.tsx b/dashboard-sqdc/src/pages/DetailPage.tsx index 1a5cc37..f6358a0 100644 --- a/dashboard-sqdc/src/pages/DetailPage.tsx +++ b/dashboard-sqdc/src/pages/DetailPage.tsx @@ -1,153 +1,264 @@ -import React from 'react'; -import { Line } from 'react-chartjs-2'; -import { KPI } from '../types'; +import React, { useState, useEffect } from 'react'; +import * as XLSX from 'xlsx'; import { getCategoryColor, getCategoryName, getCategoryEmoji } from '../data/kpiData'; +import { useSQLiteDatabase } from '../database/useSQLiteDatabase'; +import { ChartModal } from '../components/ChartModal'; +import { ExportModal } from '../components/ExportModal'; +import { RangeChartModal } from '../components/RangeChartModal'; import '../styles/DetailPage.css'; interface DetailPageProps { category: 'security' | 'quality' | 'delays' | 'costs' | 'maintenance'; - kpis: KPI[]; } -export const DetailPage: React.FC = ({ category, kpis }) => { - const [selectedKPI, setSelectedKPI] = React.useState(kpis[0] || null); +const categoryMap: Record = { + security: 1, + quality: 2, + delays: 3, + costs: 4, + maintenance: 5 +}; - const getStatusBadgeClass = (status: string) => { - return `badge-${status}`; +export const DetailPage: React.FC = ({ category }) => { + const db = useSQLiteDatabase(); + const categoryId = categoryMap[category]; + const [selectedKPIId, setSelectedKPIId] = useState(null); + const [selectedKPIMeasurements, setSelectedKPIMeasurements] = useState([]); + const [selectedKPIStats, setSelectedKPIStats] = useState(null); + const [showChart, setShowChart] = useState(false); + const [showExportModal, setShowExportModal] = useState(false); + const [showChartRangeModal, setShowChartRangeModal] = useState(false); + + // Obtenir les KPI de cette catégorie + const categoryKPIs = db.kpis.filter(kpi => kpi.category_id === categoryId); + + // Charger les mesures quand un KPI est sélectionné + useEffect(() => { + if (!selectedKPIId) { + if (categoryKPIs.length > 0) { + setSelectedKPIId(categoryKPIs[0].id); + } + return; + } + + const fetchMeasurements = async () => { + const measurements = await db.getMeasurementsForKPI(selectedKPIId, 365); + const stats = await db.getKPIStats(selectedKPIId, 30); + setSelectedKPIMeasurements(measurements || []); + setSelectedKPIStats(stats); + }; + + fetchMeasurements(); + }, [selectedKPIId, categoryKPIs, db]); + + const selectedKPI = categoryKPIs.find(k => k.id === selectedKPIId); + + // Export to Excel + const exportToExcel = (kpi: any, measurements: any[]) => { + if (!kpi || measurements.length === 0) return; + + // Préparer les données + const data = measurements.map(m => ({ + Date: new Date(m.measurement_date).toLocaleString('fr-FR'), + Valeur: m.value, + Statut: m.status, + })); + + // Créer un classeur Excel + const worksheet = XLSX.utils.json_to_sheet(data); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, 'Mesures'); + + // Ajouter une feuille de résumé + const summary = [ + ['KPI', kpi.name], + ['Unité', kpi.unit], + ['Cible', kpi.target], + ['Fréquence', kpi.frequency], + ['Nombre de mesures', measurements.length], + ['Date d\'export', new Date().toLocaleString('fr-FR')], + ]; + + const summarySheet = XLSX.utils.aoa_to_sheet(summary); + XLSX.utils.book_append_sheet(workbook, summarySheet, 'Résumé'); + + // Télécharger + const filename = `${kpi.name.replace(/\s+/g, '_')}_${new Date().toISOString().split('T')[0]}.xlsx`; + XLSX.writeFile(workbook, filename); + }; + + // Handle export with date range selection + const handleExportClick = (days: number) => { + if (!selectedKPI) return; + + let measurementsToExport = selectedKPIMeasurements; + + // Si l'utilisateur a sélectionné une plage spécifique (pas -1 pour tous) + if (days > 0 && days !== 365) { + measurementsToExport = selectedKPIMeasurements.filter((m: any) => { + const daysAgo = (Date.now() - new Date(m.measurement_date).getTime()) / (1000 * 60 * 60 * 24); + return daysAgo <= days; + }); + } + + exportToExcel(selectedKPI, measurementsToExport); + }; + + // Get measurements for chart by date range + const getMeasurementsForDateRange = (days: number) => { + if (days === -1) return selectedKPIMeasurements; + + return selectedKPIMeasurements.filter((m: any) => { + const daysAgo = (Date.now() - new Date(m.measurement_date).getTime()) / (1000 * 60 * 60 * 24); + return daysAgo <= days; + }); }; return (
-

+

{getCategoryEmoji(category)} {getCategoryName(category)}

-

Analyse détaillée des indicateurs de performance

+

Analyse détaillée des indicateurs

-
-
-

Indicateurs

-
- {kpis.map(kpi => ( -
+
+

Sélectionner un KPI

+ +
+ + +
+ +
+ {categoryKPIs.map(kpi => ( +
+
{kpi.name}
+
{kpi.unit}
+ ))}
-
- {selectedKPI ? ( - <> -
-

{selectedKPI.name}

-
- {selectedKPI.value} - {selectedKPI.unit} + {selectedKPI && ( +
+
+

{selectedKPI.name}

+
+
+
Unité
+
{selectedKPI.unit}
+
+
+
Cible
+
{selectedKPI.target}
+
+
+
Fréquence
+
{selectedKPI.frequency || 'N/A'}
- -
-
- État: - - {selectedKPI.status} - -
- - {selectedKPI.target && ( -
- Objectif: - {selectedKPI.target} {selectedKPI.unit} -
- )} - - {selectedKPI.trend && ( -
- Tendance: - - {selectedKPI.trend === 'up' && '📈 En hausse'} - {selectedKPI.trend === 'down' && '📉 En baisse'} - {selectedKPI.trend === 'stable' && '➡️ Stable'} - -
- )} -
- -
-

Description

-

{selectedKPI.description}

-
- - {selectedKPI.formula && ( -
-

Formule de Calcul

-
{selectedKPI.formula}
-
- )} - - {selectedKPI.data && selectedKPI.labels && ( -
-

Évolution sur 4 semaines

-
- -
-
- )} - -
- - - -
- - ) : ( -
-

Sélectionnez un indicateur pour voir les détails

- )} -
+ +
+

Description

+

{selectedKPI.description}

+

Formule

+

{selectedKPI.formula}

+
+ + {selectedKPIStats && ( +
+

Statistiques (30 derniers jours)

+
+
+
Moyenne
+
{selectedKPIStats.avg?.toFixed(2) || 'N/A'}
+
+
+
Min
+
{selectedKPIStats.min?.toFixed(2) || 'N/A'}
+
+
+
Max
+
{selectedKPIStats.max?.toFixed(2) || 'N/A'}
+
+
+
Mesures
+
{selectedKPIStats.count || 0}
+
+
+
+ )} + +
+

Dernières mesures ({selectedKPIMeasurements.length})

+ + + + + + + + + + {selectedKPIMeasurements.slice(-10).reverse().map((m: any, idx: number) => ( + + + + + + ))} + +
DateValeurStatut
{new Date(m.measurement_date).toLocaleString('fr-FR')}{m.value} + + {m.status === 'good' ? '✓ Bon' : m.status === 'warning' ? '⚠️ Attention' : '🔴 Critique'} + +
+
+
+ )}
+ + setShowChart(false)} + /> + + setShowExportModal(false)} + /> + + setShowChartRangeModal(false)} + />
); }; diff --git a/dashboard-sqdc/src/pages/HomePage.tsx b/dashboard-sqdc/src/pages/HomePage.tsx index 36e67e7..fa165f5 100644 --- a/dashboard-sqdc/src/pages/HomePage.tsx +++ b/dashboard-sqdc/src/pages/HomePage.tsx @@ -1,53 +1,133 @@ -import React from 'react'; -import { KPI } from '../types'; +import React, { useState, useEffect } from 'react'; import { KPICard } from '../components/KPICard'; import { getCategoryColor } from '../data/kpiData'; +import { useSQLiteDatabase } from '../database/useSQLiteDatabase'; import '../styles/HomePage.css'; -interface HomePageProps { - kpiData: { - security: KPI[]; - quality: KPI[]; - delays: KPI[]; - costs: KPI[]; - maintenance: KPI[]; - }; -} +export const HomePage: React.FC = () => { + const db = useSQLiteDatabase(); + const [timeRange, setTimeRange] = useState<'today' | 'week' | 'last7' | 'month' | 'year'>('today'); + const [stats, setStats] = useState({ total: 0, good: 0, warning: 0, critical: 0 }); + const [topKPIs, setTopKPIs] = useState({}); + const [avgPerformance, setAvgPerformance] = useState(0); + const [criticalAlerts, setCriticalAlerts] = useState([]); -export const HomePage: React.FC = ({ kpiData }) => { - // Fonction pour obtenir les KPI les plus importants par catégorie - const getTopKPIs = (category: KPI[]): KPI[] => { - return category.slice(0, 2); + // Convertir la plage de temps en nombre de jours + const getDaysFromTimeRange = (range: 'today' | 'week' | 'last7' | 'month' | 'year'): number => { + switch (range) { + case 'today': + return 0; + case 'week': { + const today = new Date(); + const dayOfWeek = today.getDay(); + const daysFromMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; + return daysFromMonday; + } + case 'last7': + return 7; + case 'month': + return 30; + case 'year': + return 365; + default: + return 0; + } }; - const topSecurityKPIs = getTopKPIs(kpiData.security); - const topQualityKPIs = getTopKPIs(kpiData.quality); - const topDelaysKPIs = getTopKPIs(kpiData.delays); - const topCostsKPIs = getTopKPIs(kpiData.costs); - const topMaintenanceKPIs = getTopKPIs(kpiData.maintenance); + // Charger les données quand les KPI changent ou que la plage change + useEffect(() => { + if (db.loading || db.kpis.length === 0) return; - // Calculer les statistiques globales - const allKPIs = [ - ...kpiData.security, - ...kpiData.quality, - ...kpiData.delays, - ...kpiData.costs, - ...kpiData.maintenance - ]; + const fetchData = async () => { + const days = getDaysFromTimeRange(timeRange); + const kpisWithStatus: any[] = []; + const alertList: any[] = []; - const stats = { - total: allKPIs.length, - good: allKPIs.filter(k => k.status === 'good').length, - warning: allKPIs.filter(k => k.status === 'warning').length, - critical: allKPIs.filter(k => k.status === 'critical').length - }; + // Charger les mesures pour chaque KPI + for (const kpi of db.kpis) { + const measurements = await db.getMeasurementsForKPI(kpi.id, days); + + let status = 'good'; + let value = 0; - const avgPerformance = Math.round( - ((stats.good * 100 + stats.warning * 50) / (stats.total * 100)) * 100 - ); + if (measurements && measurements.length > 0) { + const values = measurements.map((m: any) => m.value); + value = Math.round((values.reduce((a: number, b: number) => a + b, 0) / values.length) * 100) / 100; - // État pour la sélection de la plage temporelle - const [timeRange, setTimeRange] = React.useState<'day' | 'month' | 'year'>('month'); + const tolerance = kpi.target! * 0.1; + + if ([2, 4, 5, 6, 13, 16].includes(kpi.id)) { + if (value > kpi.target! + tolerance * 2) status = 'critical'; + else if (value > kpi.target! + tolerance) status = 'warning'; + else status = 'good'; + } else { + if (value < kpi.target! - tolerance * 2) status = 'critical'; + else if (value < kpi.target! - tolerance) status = 'warning'; + else status = 'good'; + } + + if (status === 'critical' || status === 'warning') { + alertList.push({ ...kpi, status, value }); + } + } + + kpisWithStatus.push({ ...kpi, status, value }); + } + + // Calculer les statistiques + const statCounts = { + total: kpisWithStatus.length, + good: kpisWithStatus.filter(k => k.status === 'good').length, + warning: kpisWithStatus.filter(k => k.status === 'warning').length, + critical: kpisWithStatus.filter(k => k.status === 'critical').length + }; + + // Grouper par catégorie + const topKPIsMap: any = {}; + [1, 2, 3, 4, 5].forEach(catId => { + topKPIsMap[catId] = kpisWithStatus + .filter(k => k.category_id === catId) + .slice(0, 2); + }); + + // Performance globale + const performance = Math.round( + ((statCounts.good * 100 + statCounts.warning * 50) / (statCounts.total * 100)) * 100 + ); + + setStats(statCounts); + setTopKPIs(topKPIsMap); + setAvgPerformance(performance); + setCriticalAlerts(alertList.sort((a, b) => { + if (a.status === 'critical' && b.status !== 'critical') return -1; + if (a.status !== 'critical' && b.status === 'critical') return 1; + return 0; + }).slice(0, 4)); + }; + + fetchData(); + }, [db, timeRange]); + + if (db.loading) { + return ( +
+
+

⏳ Chargement des données...

+
+
+ ); + } + + if (db.error) { + return ( +
+
+

❌ {db.error}

+

Assurez-vous que le serveur API est lancé: npm run server

+
+
+ ); + } return (
@@ -56,155 +136,112 @@ export const HomePage: React.FC = ({ kpiData }) => {

📊 Dashboard SQDC

+ +
- -
-
-
- {stats.good} -
-
KPI Bon
-
- {Math.round((stats.good / stats.total) * 100)}% des KPI + + {criticalAlerts.length > 0 && ( +
+

🚨 Alertes ({criticalAlerts.length})

+
+ {criticalAlerts.map(alert => ( +
+
+ {alert.status === 'critical' ? '🔴' : '⚠️'} +
+
+
{alert.name}
+
{alert.value} {alert.unit}
+
+
+ ))}
- -
-
- {stats.warning} -
-
À Améliorer
-
- {Math.round((stats.warning / stats.total) * 100)}% des KPI -
-
- -
-
- {stats.critical} -
-
Critique
-
- {Math.round((stats.critical / stats.total) * 100)}% des KPI -
-
- -
-
- {avgPerformance}% -
-
Performance Globale
-
Indicateur synthétique
-
-
+ )}
-
-

🎯 KPI Clés par Catégorie

- -
- {/* BLOC SYNTHÈSE */} -
-

📋 Alertes Prioritaires

-
-
- 🔴 -
-
Coût des Non-Qualité
-
18 500 €
-
-
-
- ⚠️ -
-
Taux de Retouche
-
3.8%
-
-
-
- ⚠️ -
-
Downtime
-
2.5h/jour
-
-
-
- ⚠️ -
-
OEE
-
72.3%
-
-
-
-
-
Performance Globale
-
{avgPerformance}%
-
+
+
+
+

📊 Statistiques

- -
-

🛡️ Sécurité

-
- {topSecurityKPIs.map(kpi => ( - - ))} +
+
+
{stats.total}
+
KPI Total
-
- -
-

🎯 Qualité

-
- {topQualityKPIs.map(kpi => ( - - ))} +
+
{stats.good}
+
✅ Bon
-
- -
-

⏱️ Délais & Livraison

-
- {topDelaysKPIs.map(kpi => ( - - ))} +
+
{stats.warning}
+
⚠️ Attention
-
- -
-

💰 Coûts

-
- {topCostsKPIs.map(kpi => ( - - ))} +
+
{stats.critical}
+
🔴 Critique
-
- -
-

🔧 Maintenance

-
- {topMaintenanceKPIs.map(kpi => ( - - ))} +
+
{avgPerformance}%
+
Performance
+ + {[1, 2, 3, 4, 5].map(catId => { + const categoryKPIs = topKPIs[catId] || []; + const category = db.categories.find(c => c.id === catId); + + if (!category) return null; + + return ( +
+
+

+ {category.emoji} {category.name} +

+
+
+ {categoryKPIs.map((kpi: any) => ( + + ))} +
+
+ ); + })}
); diff --git a/dashboard-sqdc/src/styles/ChartModal.css b/dashboard-sqdc/src/styles/ChartModal.css new file mode 100644 index 0000000..72de122 --- /dev/null +++ b/dashboard-sqdc/src/styles/ChartModal.css @@ -0,0 +1,104 @@ +.chart-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.chart-modal { + background: white; + border-radius: 12px; + padding: 2rem; + max-width: 90%; + width: 1000px; + max-height: 90vh; + overflow-y: auto; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + transform: translateY(-50px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.chart-modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 2px solid #f0f0f0; +} + +.chart-modal-header h2 { + margin: 0; + font-size: 1.8rem; + color: #333; +} + +.chart-modal-close { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: #999; + transition: all 0.3s ease; + padding: 0; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; +} + +.chart-modal-close:hover { + color: #333; + background: #f5f5f5; + border-radius: 4px; +} + +.chart-modal-body { + margin-bottom: 1.5rem; +} + +.chart-modal-footer { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + padding-top: 1rem; + border-top: 2px solid #f0f0f0; + color: #666; +} + +.chart-modal-footer p { + margin: 0.5rem 0; + font-size: 0.95rem; +} + +@media (max-width: 768px) { + .chart-modal { + width: 95%; + padding: 1rem; + } + + .chart-modal-body { + height: 300px; + } + + .chart-modal-footer { + grid-template-columns: 1fr; + } +} diff --git a/dashboard-sqdc/src/styles/DetailPage.css b/dashboard-sqdc/src/styles/DetailPage.css index 171e9c1..0f6d1b2 100644 --- a/dashboard-sqdc/src/styles/DetailPage.css +++ b/dashboard-sqdc/src/styles/DetailPage.css @@ -1,290 +1,325 @@ .detail-page { color: white; + padding: 2rem; } +/* DETAIL HEADER */ .detail-header { - margin-bottom: 2rem; + background: linear-gradient(135deg, rgba(100, 150, 255, 0.2) 0%, rgba(100, 100, 200, 0.1) 100%); + border-radius: 12px; + padding: 2rem; + margin-bottom: 3rem; + border-left: 5px solid rgba(255, 255, 255, 0.3); } .detail-header h1 { - font-size: 2rem; - margin-bottom: 0.5rem; + font-size: 2.2rem; + margin: 0 0 0.5rem 0; + font-weight: bold; } .detail-header p { - font-size: 1.1rem; + font-size: 1rem; opacity: 0.9; + margin: 0; } -.detail-layout { +/* DETAIL CONTENT */ +.detail-content { display: grid; - grid-template-columns: 300px 1fr; + grid-template-columns: 1fr 2fr; gap: 2rem; } -/* KPI LIST */ -.kpi-list { - background: rgba(255, 255, 255, 0.95); - border-radius: 12px; +/* KPI SELECTOR */ +.kpi-selector { + background: rgba(255, 255, 255, 0.98); + border-radius: 8px; padding: 1.5rem; color: #333; height: fit-content; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } -.kpi-list h2 { +.kpi-selector h3 { font-size: 1.2rem; - margin-bottom: 1rem; + margin: 0 0 1.2rem 0; color: #333; + font-weight: bold; } -.kpi-list-items { - display: flex; - flex-direction: column; - gap: 0.5rem; +/* ACTION BUTTONS */ +.action-buttons { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.8rem; + margin-bottom: 1.2rem; } -.kpi-list-item { - padding: 1rem; - border-left: 3px solid #ddd; - border-radius: 4px; +.btn { + padding: 0.8rem 1rem; + border: none; + border-radius: 6px; + font-weight: 600; + font-size: 0.9rem; cursor: pointer; transition: all 0.3s ease; + text-align: center; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-export { + background: #6496ff; + color: white; +} + +.btn-export:hover:not(:disabled) { + background: #4472ff; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(100, 150, 255, 0.4); +} + +.btn-chart { + background: #52d273; + color: white; +} + +.btn-chart:hover:not(:disabled) { + background: #38c459; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(82, 210, 115, 0.4); +} + +.kpi-list { + display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.kpi-option { background: white; + border: 2px solid #e0e0e0; + border-radius: 6px; + padding: 1rem; + cursor: pointer; + transition: all 0.3s ease; + text-align: left; + font-size: 0.95rem; } -.kpi-list-item:hover { - background: #f5f7fa; +.kpi-option:hover { + border-color: #6496ff; + background: #f8f9ff; } -.kpi-list-item.active { - background: #f0f2ff; +.kpi-option.active { + background: #6496ff; + color: white; + border-color: #6496ff; } -.kpi-list-name { +.kpi-name { font-weight: 600; - margin-bottom: 0.5rem; - color: #333; + margin-bottom: 0.3rem; } -.kpi-list-value { - font-size: 0.9rem; - color: #666; - margin-bottom: 0.5rem; +.kpi-unit { + font-size: 0.85rem; + opacity: 0.7; } -.badge { - display: inline-block; - padding: 0.25rem 0.6rem; - border-radius: 12px; - font-size: 0.75rem; - font-weight: 600; -} - -.badge-good { - background: #d4edda; - color: #155724; -} - -.badge-warning { - background: #fff3cd; - color: #856404; -} - -.badge-critical { - background: #f8d7da; - color: #721c24; -} - -/* DETAIL SECTION */ -.kpi-detail { - background: rgba(255, 255, 255, 0.95); - border-radius: 12px; +/* KPI DETAILS */ +.kpi-details { + background: rgba(255, 255, 255, 0.98); + border-radius: 8px; padding: 2rem; color: #333; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } -.detail-header-card { - border-top: 4px solid; +.details-header { + border-bottom: 2px solid #f0f0f0; padding-bottom: 1.5rem; margin-bottom: 1.5rem; - border-bottom: 1px solid #f0f0f0; } -.detail-header-card h2 { +.details-header h2 { font-size: 1.8rem; - margin-bottom: 1rem; - margin-top: 0; + margin: 0 0 1.2rem 0; + color: #333; } -.detail-value-section { - display: flex; - align-items: baseline; - gap: 0.5rem; +.details-info { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 1rem; } -.detail-value { - font-size: 2.5rem; +.info-item { + background: #f9f9f9; + border-left: 3px solid #6496ff; + border-radius: 4px; + padding: 1rem; +} + +.info-label { + font-size: 0.85rem; + color: #999; + font-weight: 600; + margin-bottom: 0.3rem; + text-transform: uppercase; +} + +.info-value { + font-size: 1.2rem; font-weight: bold; color: #333; } -.detail-unit { - font-size: 1rem; - color: #999; -} - -/* INFO SECTION */ -.detail-info { +/* DESCRIPTION SECTION */ +.details-description { margin-bottom: 2rem; - padding-bottom: 2rem; - border-bottom: 1px solid #f0f0f0; } -.info-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; - padding: 0.75rem; - background: #f9f9f9; - border-radius: 4px; -} - -.info-label { - font-weight: 600; - color: #666; -} - -.info-value { - color: #333; -} - -/* DESCRIPTION & FORMULA */ -.detail-description, -.detail-formula { - margin-bottom: 2rem; - padding-bottom: 2rem; - border-bottom: 1px solid #f0f0f0; -} - -.detail-description h3, -.detail-formula h3 { +.details-description h4 { font-size: 1.1rem; - font-weight: 600; - margin-bottom: 1rem; color: #333; + margin: 1.2rem 0 0.6rem 0; + font-weight: bold; } -.detail-description p { +.details-description p { color: #666; line-height: 1.6; margin: 0; } -.detail-formula pre { - background: #f5f7fa; - padding: 1rem; - border-radius: 4px; - overflow-x: auto; - font-size: 0.9rem; - line-height: 1.4; - color: #333; +/* STATISTICS SECTION */ +.details-stats { + background: #f9f9f9; + border-radius: 6px; + padding: 1.5rem; + margin: 2rem 0; } -/* CHART DETAIL */ -.detail-chart { - margin-bottom: 2rem; - padding-bottom: 2rem; - border-bottom: 1px solid #f0f0f0; -} - -.detail-chart h3 { +.details-stats h4 { font-size: 1.1rem; - font-weight: 600; - margin-bottom: 1rem; color: #333; + margin: 0 0 1rem 0; + font-weight: bold; } -.chart-container-detail { - position: relative; - height: 350px; - margin-bottom: 1rem; -} - -/* ACTIONS */ -.detail-actions { - display: flex; +.stats-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1rem; } -.btn { - padding: 0.75rem 1.5rem; - border: none; +.stat-box { + background: white; + border: 1px solid #e0e0e0; border-radius: 6px; - font-size: 0.95rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; + padding: 1rem; + text-align: center; } -.btn-primary { - background: #667eea; - color: white; +.stat-box-label { + font-size: 0.85rem; + color: #999; + margin-bottom: 0.5rem; } -.btn-primary:hover { - background: #764ba2; -} - -.btn-secondary { - background: #f0f0f0; +.stat-box-value { + font-size: 1.5rem; + font-weight: bold; color: #333; } -.btn-secondary:hover { - background: #e0e0e0; +/* MEASUREMENTS TABLE */ +.measurements-section { + margin-top: 2rem; } -.no-selection { - text-align: center; - padding: 3rem; - color: #999; +.measurements-section h4 { font-size: 1.1rem; + color: #333; + margin: 0 0 1rem 0; + font-weight: bold; +} + +.measurements-table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: 6px; + overflow: hidden; +} + +.measurements-table thead { + background: #f9f9f9; +} + +.measurements-table th { + padding: 1rem; + text-align: left; + font-weight: bold; + color: #333; + border-bottom: 2px solid #e0e0e0; +} + +.measurements-table td { + padding: 0.8rem 1rem; + border-bottom: 1px solid #e0e0e0; + color: #666; +} + +.measurements-table tbody tr:hover { + background: #f9f9f9; +} + +.status-badge { + display: inline-block; + padding: 0.4rem 0.8rem; + border-radius: 4px; + font-size: 0.85rem; + font-weight: 600; +} + +.status-good { + background: #d4edda; + color: #155724; +} + +.status-warning { + background: #fff3cd; + color: #856404; +} + +.status-critical { + background: #f8d7da; + color: #721c24; } /* RESPONSIVE */ @media (max-width: 1024px) { - .detail-layout { + .detail-content { grid-template-columns: 1fr; } + .kpi-selector { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.5rem; + height: auto; + } + .kpi-list { display: grid; - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - height: auto; - } - - .kpi-list-items { - grid-column: 1 / -1; - display: grid; - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - } -} - -@media (max-width: 768px) { - .detail-header h1 { - font-size: 1.5rem; - } - - .detail-value { - font-size: 2rem; - } - - .detail-actions { - flex-direction: column; - } - - .btn { - width: 100%; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 0.5rem; } } diff --git a/dashboard-sqdc/src/styles/ExportModal.css b/dashboard-sqdc/src/styles/ExportModal.css new file mode 100644 index 0000000..6163613 --- /dev/null +++ b/dashboard-sqdc/src/styles/ExportModal.css @@ -0,0 +1,168 @@ +.export-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1001; +} + +.export-modal { + background: white; + border-radius: 12px; + padding: 2rem; + max-width: 500px; + width: 90%; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + transform: translateY(-50px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.export-modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 2px solid #f0f0f0; +} + +.export-modal-header h2 { + margin: 0; + font-size: 1.5rem; + color: #333; +} + +.export-modal-close { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: #999; + transition: all 0.3s ease; + padding: 0; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; +} + +.export-modal-close:hover { + color: #333; + background: #f5f5f5; + border-radius: 4px; +} + +.export-modal-body { + margin-bottom: 1.5rem; +} + +.export-modal-description { + color: #666; + font-size: 0.95rem; + margin-bottom: 1.5rem; +} + +.export-options { + display: grid; + grid-template-columns: 1fr; + gap: 0.8rem; +} + +.export-option { + display: flex; + flex-direction: column; + gap: 0.4rem; + padding: 1rem; + border: 2px solid #e0e0e0; + border-radius: 8px; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.export-option input { + display: none; +} + +.export-option:hover { + border-color: #6496ff; + background: #f8f9ff; +} + +.export-option.active { + background: #f0f4ff; + border-color: #6496ff; +} + +.export-option .option-label { + font-weight: 600; + color: #333; + font-size: 1rem; +} + +.export-option .option-sublabel { + font-size: 0.85rem; + color: #999; +} + +.export-modal-footer { + display: flex; + gap: 1rem; + justify-content: flex-end; + padding-top: 1rem; + border-top: 2px solid #f0f0f0; +} + +.btn-cancel { + padding: 0.8rem 1.5rem; + border: 2px solid #e0e0e0; + background: white; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + color: #666; +} + +.btn-cancel:hover { + border-color: #ccc; + background: #f9f9f9; +} + +.btn-export-confirm { + padding: 0.8rem 1.5rem; + background: #6496ff; + color: white; + border: none; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; +} + +.btn-export-confirm:hover:not(:disabled) { + background: #4472ff; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(100, 150, 255, 0.4); +} + +.btn-export-confirm:disabled { + opacity: 0.5; + cursor: not-allowed; +} diff --git a/dashboard-sqdc/src/styles/HomePage.css b/dashboard-sqdc/src/styles/HomePage.css index d32b99c..62be372 100644 --- a/dashboard-sqdc/src/styles/HomePage.css +++ b/dashboard-sqdc/src/styles/HomePage.css @@ -51,20 +51,19 @@ color: white; } -.stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 1rem; - margin-bottom: 1.5rem; +/* STAT CARDS - UNIFIED WITH CATEGORIES */ +.stats-cards { + display: contents; } .stat-card { - background: rgba(255, 255, 255, 0.95); + background: rgba(255, 255, 255, 0.98); border-radius: 8px; - padding: 1.2rem; + padding: 1.5rem; text-align: center; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; + color: #333; } .stat-card:hover { @@ -72,22 +71,16 @@ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); } -.stat-value { +.stat-number { font-size: 2.2rem; font-weight: bold; - margin-bottom: 0.3rem; + margin-bottom: 0.5rem; } .stat-label { - font-size: 0.85rem; + font-size: 0.9rem; font-weight: 600; - color: #333; - margin-bottom: 0.3rem; -} - -.stat-description { - font-size: 0.75rem; - color: #999; + color: #555; } /* TOP KPIs SECTION */ @@ -105,10 +98,140 @@ } /* CATEGORIES GRID - CÔTE À CÔTE */ -.categories-grid { +.main-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 1.5rem; + margin-top: 2rem; +} + +/* CATEGORY SECTION */ +.category-section { + background: rgba(255, 255, 255, 0.98); + border-radius: 8px; + padding: 1.5rem; + color: #333; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; +} + +.category-section:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); +} + +.category-header { + margin-bottom: 1.2rem; +} + +.category-header h2 { + font-size: 1.3rem; + margin: 0; + color: #333; +} + +/* GRILLE DES STATS À L'INTÉRIEUR DU BLOC */ +.stats-grid-inner { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.2rem; +} + +.stat-card-inner { + display: flex; + align-items: center; + gap: 1rem; + background: rgba(100, 150, 255, 0.08); + border-radius: 6px; + padding: 1.2rem; + border: 1px solid rgba(100, 150, 255, 0.15); +} + +.stat-card-inner:last-child { + grid-column: 1 / -1; + justify-content: center; + gap: 1rem; +} + +.stat-card-inner .stat-number { + font-size: 2rem; + font-weight: bold; + color: #333; + min-width: auto; +} + +.stat-card-inner .stat-label { + font-size: 0.85rem; + font-weight: 600; + color: #666; + flex: none; +} + +/* STAT CARDS - UNIFIED WITH CATEGORIES */ +.stats-cards { + display: contents; +} + +.stat-card { + background: rgba(255, 255, 255, 0.98); + border-radius: 8px; + padding: 1.5rem; + text-align: center; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + color: #333; +} + +.stat-card:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); +} + +.stat-number { + font-size: 2.2rem; + font-weight: bold; + margin-bottom: 0.5rem; +} + +.stat-label { + font-size: 0.9rem; + font-weight: 600; + color: #555; +} + +/* TOP KPIs SECTION */ +.top-kpis-section { + background: rgba(255, 255, 255, 0.98); + border-radius: 8px; + padding: 1.5rem; + color: #333; +} + +.top-kpis-section h2 { + font-size: 1.3rem; + margin-bottom: 1.2rem; + color: #333; +} + +/* CATEGORY SECTION */ +.category-section { + background: rgba(255, 255, 255, 0.98); + border-radius: 8px; + padding: 1.5rem; + color: #333; +} + +.category-header h2 { + font-size: 1.2rem; + margin: 0 0 1rem 0; + padding-bottom: 0.8rem; + border-bottom: 2px solid rgba(0, 0, 0, 0.1); +} + +.kpi-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 1rem; } /* SUMMARY SECTION */ @@ -154,6 +277,11 @@ background: rgba(243, 156, 18, 0.05); } +.alert-item.good { + border-left-color: #27ae60; + background: rgba(39, 174, 96, 0.05); +} + .alert-icon { font-size: 1.2rem; flex-shrink: 0; diff --git a/dashboard-sqdc/src/styles/RangeChartModal.css b/dashboard-sqdc/src/styles/RangeChartModal.css new file mode 100644 index 0000000..25e35e4 --- /dev/null +++ b/dashboard-sqdc/src/styles/RangeChartModal.css @@ -0,0 +1,152 @@ +.range-chart-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.range-chart-modal { + background: white; + border-radius: 12px; + padding: 2rem; + max-width: 90%; + width: 1100px; + max-height: 90vh; + overflow-y: auto; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + transform: translateY(-50px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.range-chart-modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 2px solid #f0f0f0; +} + +.range-chart-modal-header h2 { + margin: 0; + font-size: 1.8rem; + color: #333; +} + +.range-chart-modal-close { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: #999; + transition: all 0.3s ease; + padding: 0; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; +} + +.range-chart-modal-close:hover { + color: #333; + background: #f5f5f5; + border-radius: 4px; +} + +/* RANGE SELECTOR */ +.range-chart-modal-range-selector { + display: flex; + gap: 0.8rem; + margin-bottom: 1.5rem; + flex-wrap: wrap; +} + +.range-option { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.6rem 1rem; + border: 2px solid #e0e0e0; + border-radius: 6px; + cursor: pointer; + transition: all 0.3s ease; + background: white; + font-weight: 600; + color: #333; +} + +.range-option input { + display: none; +} + +.range-option:hover { + border-color: #6496ff; + background: #f8f9ff; +} + +.range-option.active { + background: #6496ff; + color: white; + border-color: #6496ff; +} + +.range-chart-modal-body { + margin-bottom: 1.5rem; + background: #f9f9f9; + border-radius: 8px; + padding: 1rem; +} + +.range-chart-modal-footer { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + padding-top: 1rem; + border-top: 2px solid #f0f0f0; + color: #666; +} + +.range-chart-modal-footer p { + margin: 0.5rem 0; + font-size: 0.95rem; +} + +@media (max-width: 768px) { + .range-chart-modal { + width: 95%; + padding: 1rem; + } + + .range-chart-modal-body { + height: 300px; + } + + .range-chart-modal-range-selector { + flex-direction: column; + } + + .range-option { + flex: 1; + } + + .range-chart-modal-footer { + grid-template-columns: 1fr; + } +}