Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d94cf23e24 | |||
| 14897e198c | |||
| 55af4eb8dc | |||
| 56dc1f2256 | |||
| ee7188c92c | |||
| 2d4341311d | |||
| 476b227428 |
@@ -22,3 +22,5 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
.env
|
||||||
Generated
+13
@@ -13,6 +13,7 @@
|
|||||||
"@mui/icons-material": "^6.3.0",
|
"@mui/icons-material": "^6.3.0",
|
||||||
"@mui/material": "^6.3.0",
|
"@mui/material": "^6.3.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
@@ -4451,6 +4452,18 @@
|
|||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
|
||||||
|
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"@mui/icons-material": "^6.3.0",
|
"@mui/icons-material": "^6.3.0",
|
||||||
"@mui/material": "^6.3.0",
|
"@mui/material": "^6.3.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
.critical-alert-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-alert-box {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import "./CriticalAlert.css";
|
||||||
|
|
||||||
|
const CriticalAlert = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const [isActive, setIsActive] = useState(false);
|
||||||
|
const [opacity, setOpacity] = useState(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateOpacity = () => {
|
||||||
|
const alertStatus = localStorage.getItem("criticalAlertActive");
|
||||||
|
const startTime = localStorage.getItem("criticalAlertStartTime");
|
||||||
|
const totalDuration = parseInt(localStorage.getItem("totalDuration") || "15", 10);
|
||||||
|
|
||||||
|
if (location.pathname === "/backtime") {
|
||||||
|
setOpacity(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alertStatus === "yes" && startTime) {
|
||||||
|
setIsActive(true);
|
||||||
|
|
||||||
|
const startTimestamp = parseInt(startTime, 10);
|
||||||
|
const now = Date.now();
|
||||||
|
const elapsedTime = now - startTimestamp;
|
||||||
|
const totalTime = totalDuration * 24 * 60 * 60 * 1000;
|
||||||
|
const remainingTime = totalTime - elapsedTime;
|
||||||
|
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
setOpacity(0);
|
||||||
|
} else {
|
||||||
|
const newOpacity = 1 - elapsedTime / totalTime;
|
||||||
|
setOpacity(newOpacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateOpacity();
|
||||||
|
const interval = setInterval(updateOpacity, 10000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
isActive && location.pathname !== "/backtime" && (
|
||||||
|
<style>
|
||||||
|
{`body { opacity: ${opacity}; transition: opacity 10s linear; }`}
|
||||||
|
</style>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CriticalAlert;
|
||||||
@@ -210,7 +210,7 @@ const Header = () => {
|
|||||||
...commonButtonStyles,
|
...commonButtonStyles,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
À propos
|
À Propos
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
const AdminDashboard = () => {
|
||||||
|
const [isActive, setIsActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const alertStatus = localStorage.getItem("criticalAlertActive");
|
||||||
|
setIsActive(alertStatus === "yes");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const activateAlert = () => {
|
||||||
|
localStorage.setItem("criticalAlertActive", "yes");
|
||||||
|
localStorage.setItem("criticalAlertStartTime", Date.now().toString());
|
||||||
|
setIsActive(true);
|
||||||
|
window.location.reload(); // Recharge la page pour appliquer les changements
|
||||||
|
};
|
||||||
|
|
||||||
|
const deactivateAlert = () => {
|
||||||
|
localStorage.removeItem("criticalAlertActive");
|
||||||
|
localStorage.removeItem("criticalAlertStartTime");
|
||||||
|
setIsActive(false);
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Panneau Administrateur</h2>
|
||||||
|
<button
|
||||||
|
onClick={activateAlert}
|
||||||
|
style={{ backgroundColor: "red", color: "white", padding: "10px", marginRight: "10px" }}
|
||||||
|
disabled={isActive}
|
||||||
|
>
|
||||||
|
Activer l'Alerte Critique
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={deactivateAlert}
|
||||||
|
style={{ backgroundColor: "green", color: "white", padding: "10px" }}
|
||||||
|
disabled={!isActive}
|
||||||
|
>
|
||||||
|
Désactiver l'Alerte Critique
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AdminDashboard;
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
/* 🔥 Design moderne pour la page Backtime */
|
||||||
|
.backtime-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
background: linear-gradient(135deg, #1e1e2f, #3b3b58);
|
||||||
|
color: white;
|
||||||
|
font-family: "Poppins", sans-serif;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-button {
|
||||||
|
background: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-button:hover {
|
||||||
|
background: #c0392b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown h3 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #f1c40f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-setting {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-setting label {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-setting input {
|
||||||
|
width: 60px;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-container span {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🎚️ Switch */
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: 0.4s;
|
||||||
|
border-radius: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
transition: 0.4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider {
|
||||||
|
background-color: #2ecc71;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
transform: translateX(26px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🔐 Modernisation du formulaire de connexion */
|
||||||
|
.login-container {
|
||||||
|
background: rgba(0, 0, 0, 0.8); /* Fond semi-transparent pour un effet élégant */
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0px 10px 25px rgba(0, 0, 0, 0.3); /* Ombre plus profonde pour effet moderne */
|
||||||
|
backdrop-filter: blur(10px); /* Effet flou pour un look premium */
|
||||||
|
width: 320px;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container:hover {
|
||||||
|
transform: scale(1.02); /* Effet léger d’agrandissement au survol */
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container h2 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #f1c40f; /* Couleur dorée pour mettre en avant le titre */
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container input,
|
||||||
|
.login-button {
|
||||||
|
width: 100%; /* Assure que les deux éléments prennent la même largeur */
|
||||||
|
max-width: 280px; /* Définit une largeur max pour éviter qu'ils deviennent trop grands */
|
||||||
|
display: block; /* Évite les décalages */
|
||||||
|
margin: 0 auto; /* Centre les éléments */
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container input {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container input::placeholder {
|
||||||
|
color: #ddd;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
background: linear-gradient(135deg, #3498db, #2980b9);
|
||||||
|
color: white;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-top: 10px; /* Ajoute un petit espace entre le champ et le bouton */
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:hover {
|
||||||
|
background: linear-gradient(135deg, #2980b9, #1c6ea4);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🎨 Design amélioré du menu déroulant */
|
||||||
|
.duration-selector {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration-selector label {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #f0c040;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🌟 Style du select */
|
||||||
|
.duration-selector select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
background: linear-gradient(135deg, #222831, #393e46);
|
||||||
|
border: 2px solid #f0c040;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🎨 Effet au survol */
|
||||||
|
.duration-selector select:hover {
|
||||||
|
background: linear-gradient(135deg, #393e46, #222831);
|
||||||
|
border-color: #ffcc00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🔽 Apparence des options */
|
||||||
|
.duration-selector select option {
|
||||||
|
background: #222831;
|
||||||
|
color: white;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import "./Backtime.css";
|
||||||
|
|
||||||
|
const PASSWORD_HASH = import.meta.env.VITE_PASSWORD_HASH;
|
||||||
|
|
||||||
|
const Backtime = () => {
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
const [inputPassword, setInputPassword] = useState("");
|
||||||
|
const [timeRemaining, setTimeRemaining] = useState(null);
|
||||||
|
const [selectedDuration, setSelectedDuration] = useState(() => {
|
||||||
|
return parseInt(localStorage.getItem("criticalAlertDuration")) || 15;
|
||||||
|
});
|
||||||
|
const [isActive, setIsActive] = useState(() => {
|
||||||
|
return localStorage.getItem("criticalAlertActive") === "yes";
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const storedAuth = localStorage.getItem("backtimeAuth");
|
||||||
|
if (storedAuth === "true") {
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimeRemaining();
|
||||||
|
const interval = setInterval(updateTimeRemaining, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isActive) {
|
||||||
|
localStorage.setItem("criticalAlertDuration", selectedDuration);
|
||||||
|
if (!localStorage.getItem("criticalAlertStartTime")) {
|
||||||
|
localStorage.setItem("criticalAlertStartTime", Date.now().toString());
|
||||||
|
}
|
||||||
|
updateTimeRemaining();
|
||||||
|
}
|
||||||
|
}, [selectedDuration]);
|
||||||
|
|
||||||
|
const hashPassword = async (password) => {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const data = encoder.encode(password);
|
||||||
|
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
||||||
|
return Array.from(new Uint8Array(hashBuffer))
|
||||||
|
.map((b) => b.toString(16).padStart(2, "0"))
|
||||||
|
.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
const hashedInput = await hashPassword(inputPassword);
|
||||||
|
if (hashedInput === PASSWORD_HASH) {
|
||||||
|
localStorage.setItem("backtimeAuth", "true");
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
} else {
|
||||||
|
alert("Mot de passe incorrect !");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
localStorage.removeItem("backtimeAuth");
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTimeRemaining = () => {
|
||||||
|
const startTime = localStorage.getItem("criticalAlertStartTime");
|
||||||
|
|
||||||
|
if (startTime) {
|
||||||
|
const startTimestamp = parseInt(startTime, 10);
|
||||||
|
const now = Date.now();
|
||||||
|
const elapsedTime = now - startTimestamp;
|
||||||
|
const totalTime = selectedDuration * 24 * 60 * 60 * 1000;
|
||||||
|
const remainingTime = totalTime - elapsedTime;
|
||||||
|
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
setTimeRemaining("Le site est complètement transparent !");
|
||||||
|
localStorage.setItem("criticalAlertActive", "no"); // Désactive le module si le temps est écoulé
|
||||||
|
} else {
|
||||||
|
const days = Math.floor(remainingTime / (1000 * 60 * 60 * 24));
|
||||||
|
const hours = Math.floor((remainingTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
|
const minutes = Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000);
|
||||||
|
setTimeRemaining(`${days}j ${hours}h ${minutes}m ${seconds}s`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTimeRemaining("Aucune alerte activée");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDurationChange = (event) => {
|
||||||
|
const newDuration = parseInt(event.target.value, 10);
|
||||||
|
setSelectedDuration(newDuration);
|
||||||
|
if (isActive) {
|
||||||
|
localStorage.setItem("criticalAlertDuration", newDuration);
|
||||||
|
if (!localStorage.getItem("criticalAlertStartTime")) {
|
||||||
|
localStorage.setItem("criticalAlertStartTime", Date.now().toString());
|
||||||
|
}
|
||||||
|
updateTimeRemaining();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="backtime-container">
|
||||||
|
<Helmet>
|
||||||
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
|
<title>Page introuvable</title>
|
||||||
|
<style>{`body { opacity: 1 !important; }`}</style>
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
{isAuthenticated ? (
|
||||||
|
<>
|
||||||
|
<h2>Gestion du module d'alerte critique</h2>
|
||||||
|
<button onClick={handleLogout} className="logout-button">Déconnexion</button>
|
||||||
|
|
||||||
|
<div className="switch-container">
|
||||||
|
<SwitchControl isActive={isActive} setIsActive={setIsActive} selectedDuration={selectedDuration} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="duration-selector">
|
||||||
|
<label>Durée du processus :</label>
|
||||||
|
<select value={selectedDuration} onChange={handleDurationChange}>
|
||||||
|
{Array.from({ length: 15 }, (_, i) => i + 1).map((day) => (
|
||||||
|
<option key={day} value={day}>{day} jours</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="countdown">
|
||||||
|
<h3>Temps restant avant transparence totale :</h3>
|
||||||
|
<p>{timeRemaining}</p>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="login-container">
|
||||||
|
<h2>Accès Restreint</h2>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Entrez le mot de passe"
|
||||||
|
value={inputPassword}
|
||||||
|
onChange={(e) => setInputPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button onClick={handleLogin} className="login-button">Valider</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SwitchControl = ({ isActive, setIsActive, selectedDuration }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const alertStatus = localStorage.getItem("criticalAlertActive");
|
||||||
|
setIsActive(alertStatus === "yes");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleAlert = () => {
|
||||||
|
if (!isActive) {
|
||||||
|
localStorage.setItem("criticalAlertActive", "yes");
|
||||||
|
if (!localStorage.getItem("criticalAlertStartTime")) {
|
||||||
|
localStorage.setItem("criticalAlertStartTime", Date.now().toString());
|
||||||
|
}
|
||||||
|
localStorage.setItem("criticalAlertDuration", selectedDuration);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("criticalAlertActive", "no");
|
||||||
|
localStorage.removeItem("criticalAlertStartTime");
|
||||||
|
}
|
||||||
|
setIsActive(!isActive);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="switch-container">
|
||||||
|
<label className="switch">
|
||||||
|
<input type="checkbox" checked={isActive} onChange={toggleAlert} />
|
||||||
|
<span className="slider"></span>
|
||||||
|
</label>
|
||||||
|
<span className={`status-indicator ${isActive ? "active" : "inactive"}`}>
|
||||||
|
{isActive ? "Module Actif" : "Module Inactif"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Backtime;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
.notfound-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notfound-container h1 {
|
||||||
|
font-size: 100px;
|
||||||
|
color: red;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notfound-container h2 {
|
||||||
|
font-size: 30px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notfound-container p {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-home {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #007BFF;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 18px;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-home:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import "./NotFound.css"; // Style dédié
|
||||||
|
|
||||||
|
const NotFound = () => {
|
||||||
|
return (
|
||||||
|
<div className="notfound-container">
|
||||||
|
<h1>404</h1>
|
||||||
|
<h2>Oups ! Page introuvable</h2>
|
||||||
|
<p>Il semble que la page que vous recherchez n'existe pas.</p>
|
||||||
|
<Link to="/" className="back-home">Retour à l'accueil</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotFound;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const SwitchControl = () => {
|
||||||
|
const [isActive, setIsActive] = useState(() => {
|
||||||
|
return localStorage.getItem("criticalAlertActive") === "yes";
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const alertStatus = localStorage.getItem("criticalAlertActive");
|
||||||
|
setIsActive(alertStatus === "yes");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleAlert = () => {
|
||||||
|
if (!isActive) {
|
||||||
|
localStorage.setItem("criticalAlertActive", "yes");
|
||||||
|
localStorage.setItem("criticalAlertStartTime", Date.now().toString());
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("criticalAlertActive", "no");
|
||||||
|
localStorage.removeItem("criticalAlertStartTime");
|
||||||
|
}
|
||||||
|
setIsActive(!isActive);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="switch-container">
|
||||||
|
<label className="switch">
|
||||||
|
<input type="checkbox" checked={isActive} onChange={toggleAlert} />
|
||||||
|
<span className="slider"></span>
|
||||||
|
</label>
|
||||||
|
<span className={`status-indicator ${isActive ? "active" : "inactive"}`}>
|
||||||
|
{isActive ? "Module Actif" : "Module Inactif"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+10
-1
@@ -5,9 +5,13 @@ import { ThemeProvider } from "@mui/material/styles";
|
|||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import { HelmetProvider } from "react-helmet-async";
|
import { HelmetProvider } from "react-helmet-async";
|
||||||
import SimpleReactLightbox from "simple-react-lightbox";
|
import SimpleReactLightbox from "simple-react-lightbox";
|
||||||
|
import CriticalAlert from "./components/CriticalAlert";
|
||||||
|
import AdminDashboard from "./components/Pages/AdminDashboard";
|
||||||
|
import Backtime from "./components/Pages/Backtime";
|
||||||
|
import NotFound from "./components/Pages/NotFound";
|
||||||
|
|
||||||
// Lazy loading des pages
|
// Lazy loading des pages
|
||||||
const Home = lazy(() => import('./components/pages/Home.jsx'));
|
const Home = lazy(() => import('./components/Pages/Home.jsx'));
|
||||||
const About = lazy(() => import('./components/Pages/About'));
|
const About = lazy(() => import('./components/Pages/About'));
|
||||||
const Contact = lazy(() => import('./components/Pages/Contact'));
|
const Contact = lazy(() => import('./components/Pages/Contact'));
|
||||||
const Post = lazy(() => import('./components/Pages/Post'));
|
const Post = lazy(() => import('./components/Pages/Post'));
|
||||||
@@ -27,6 +31,7 @@ function YourApp() {
|
|||||||
<Suspense fallback={<div>Chargement...</div>}>
|
<Suspense fallback={<div>Chargement...</div>}>
|
||||||
<Router>
|
<Router>
|
||||||
<Header />
|
<Header />
|
||||||
|
<CriticalAlert />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/posts" element={<Post />} />
|
<Route path="/posts" element={<Post />} />
|
||||||
@@ -37,6 +42,9 @@ function YourApp() {
|
|||||||
<Route path="/services/prestation-maitrise-oeuvre" element={<ServiceUn />} />
|
<Route path="/services/prestation-maitrise-oeuvre" element={<ServiceUn />} />
|
||||||
<Route path="/services/formation-ia" element={<ServiceDeux />} />
|
<Route path="/services/formation-ia" element={<ServiceDeux />} />
|
||||||
<Route path="/services/formation-video" element={<ServiceTrois />} />
|
<Route path="/services/formation-video" element={<ServiceTrois />} />
|
||||||
|
<Route path="/backtime" element={<Backtime />} />
|
||||||
|
<Route path="/admin" element={<AdminDashboard />} />
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Router>
|
</Router>
|
||||||
@@ -44,6 +52,7 @@ function YourApp() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
|
|||||||
Reference in New Issue
Block a user