Ajout des écoles + changement CSS
This commit is contained in:
@@ -58,3 +58,51 @@ ALTER TABLE projects
|
|||||||
ADD COLUMN status VARCHAR(100) NULL AFTER title;
|
ADD COLUMN status VARCHAR(100) NULL AFTER title;
|
||||||
ALTER TABLE experiences
|
ALTER TABLE experiences
|
||||||
ADD COLUMN status VARCHAR(100) NULL AFTER title;
|
ADD COLUMN status VARCHAR(100) NULL AFTER title;
|
||||||
|
|
||||||
|
-- Table de réglages du site (clé / valeur) : utilisée pour la notification de la page d'accueil
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
name VARCHAR(50) NOT NULL,
|
||||||
|
value TEXT NULL,
|
||||||
|
PRIMARY KEY (name)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Valeurs par défaut de la notification (modifiables ensuite depuis le panel Admin)
|
||||||
|
INSERT INTO settings (name, value) VALUES
|
||||||
|
('site_notice_title', 'Des bugs peuvent encore se produire.'),
|
||||||
|
('site_notice_text', 'Si le CSS ou les images s''affichent mal, pensez à vider le cache de votre navigateur (Ctrl + F5 ou Cmd + Shift + R) pour voir les dernières modifications.'),
|
||||||
|
('site_notice_enabled', '1')
|
||||||
|
ON DUPLICATE KEY UPDATE value = VALUES(value);
|
||||||
|
|
||||||
|
-- Table des compétences : une ligne = une catégorie (carte sur la page Compétences)
|
||||||
|
-- items : un item par ligne, au format "classes-icone-fontawesome|Texte" (l'icône est optionnelle : "Texte" seul fonctionne aussi)
|
||||||
|
CREATE TABLE IF NOT EXISTS skills (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
title VARCHAR(100) NOT NULL,
|
||||||
|
icon VARCHAR(100) NULL,
|
||||||
|
items TEXT NOT NULL,
|
||||||
|
position INT NOT NULL DEFAULT 0,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Compétences actuelles (reprises du CV)
|
||||||
|
INSERT INTO skills (title, icon, items, position) VALUES
|
||||||
|
('Systèmes d''exploitation', 'fa-solid fa-computer', 'fa-brands fa-microsoft|Windows (Serveur/XP/8/10/11)\nfa-brands fa-linux|Linux (Debian/Ubuntu)\nfa-brands fa-apple|macOS', 1),
|
||||||
|
('Réseaux', 'fa-solid fa-network-wired', 'fa-solid fa-location-dot|Adressage IP\nfa-solid fa-diagram-project|VLAN\nfa-solid fa-shield-halved|Pare-feu (iptables/ufw)\nfa-solid fa-globe|DNS (Bind9)\nfa-solid fa-server|DHCP (ISC DHCP)\nfa-solid fa-arrows-left-right|Proxy (Squid)', 2),
|
||||||
|
('Scripting', 'fa-solid fa-terminal', 'fa-solid fa-terminal|Bash\nfa-brands fa-python|Python\nfa-brands fa-java|Java\nfa-solid fa-gears|Ansible', 3),
|
||||||
|
('Applications', 'fa-solid fa-cubes', 'fa-solid fa-address-book|LDAP (Windows AD)\nfa-solid fa-headset|GLPI\nfa-solid fa-compact-disc|FOG\nfa-solid fa-server|Nginx / Apache\nfa-brands fa-docker|Docker\nfa-solid fa-display|Guacamole\nfa-solid fa-database|MySQL / MariaDB', 4),
|
||||||
|
('Développement web', 'fa-solid fa-code', 'fa-brands fa-html5|HTML\nfa-brands fa-css3-alt|CSS\nfa-brands fa-js|JavaScript\nfa-brands fa-php|PHP', 5),
|
||||||
|
('Virtualisation & Outils', 'fa-solid fa-layer-group', 'fa-solid fa-server|Proxmox\nfa-solid fa-box|VirtualBox\nfa-brands fa-git-alt|Git (GitHub, Gitea)', 6);
|
||||||
|
|
||||||
|
-- Table des écoles / formations : affichée sous les expériences professionnelles sur la page Expériences
|
||||||
|
CREATE TABLE IF NOT EXISTS schools (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
title VARCHAR(100) NOT NULL,
|
||||||
|
status VARCHAR(100) NULL,
|
||||||
|
tags VARCHAR(255) NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
duration VARCHAR(100) NOT NULL,
|
||||||
|
image_url VARCHAR(255) NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
@@ -20,5 +20,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- Apparition des cartes au défilement (IntersectionObserver) -->
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
// On respecte la préférence "réduire les animations" du système
|
||||||
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||||
|
if (!('IntersectionObserver' in window)) return;
|
||||||
|
|
||||||
|
// Éléments qui apparaissent en glissant vers le haut quand ils entrent dans l'écran
|
||||||
|
var cibles = document.querySelectorAll(
|
||||||
|
'.project-card, .quick-link-card, .skill-category, .contact-card, ' +
|
||||||
|
'.login-card, .project-form-card, .cgu-card, ' +
|
||||||
|
'.page-header, .quick-links-header, .skills-header, .projects-title'
|
||||||
|
);
|
||||||
|
|
||||||
|
var observer = new IntersectionObserver(function (entries) {
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('visible');
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
|
||||||
|
|
||||||
|
cibles.forEach(function (el, i) {
|
||||||
|
el.classList.add('reveal');
|
||||||
|
// Petit décalage en cascade pour les éléments d'une même grille
|
||||||
|
el.style.setProperty('--reveal-delay', ((i % 4) * 0.08) + 's');
|
||||||
|
observer.observe(el);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Ajout de connexion a la bdd
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Fonction qui va faire une requête SQL afin de collecter toutes les écoles / formations
|
||||||
|
// Retourne un tableau vide si la table n'existe pas encore (pas de die() pour que la page
|
||||||
|
// Expériences continue de fonctionner tant que le script SQL n'a pas été exécuté)
|
||||||
|
function listeEcoles() {
|
||||||
|
global $dbh;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $dbh->prepare('SELECT id, title, status, tags, description, duration, image_url FROM schools ORDER BY id DESC');
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction qui permet de récupérer une école par id (pour la modification dans le panel Admin)
|
||||||
|
function recupererEcoleParId($id) {
|
||||||
|
global $dbh;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $dbh->prepare('SELECT id, title, status, tags, description, duration, image_url FROM schools WHERE id = :id');
|
||||||
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction interne qui affiche une carte école (utilisée par la page publique et le panel admin)
|
||||||
|
function afficherEcoleCard($ecole, $admin = false) {
|
||||||
|
$id = (int) $ecole['id'];
|
||||||
|
$titre = htmlspecialchars($ecole['title']);
|
||||||
|
$description = nl2br(htmlspecialchars($ecole['description']));
|
||||||
|
$duration = htmlspecialchars($ecole['duration']);
|
||||||
|
$image = htmlspecialchars($ecole['image_url']);
|
||||||
|
$status = htmlspecialchars($ecole['status'] ?? '');
|
||||||
|
$tags = [];
|
||||||
|
|
||||||
|
// S'il y a des tags alors on les sépares grâce à la virgule (tags1,tags2,etc...)
|
||||||
|
if (!empty($ecole['tags'])) {
|
||||||
|
$tags = array_filter(array_map('trim', explode(',', $ecole['tags'])));
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<article class="project-card">';
|
||||||
|
|
||||||
|
// Si la donnée avec l'image n'est pas null alors on l'affiche
|
||||||
|
if (!empty($image)) {
|
||||||
|
echo '<div class="project-banner">';
|
||||||
|
echo '<img src="' . $image . '" alt="Image de l ecole ' . $titre . '">';
|
||||||
|
|
||||||
|
if (!empty($status)) {
|
||||||
|
echo '<div class="project-state">' . $status . '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="project-content">';
|
||||||
|
echo '<h2>' . $titre . '</h2>';
|
||||||
|
|
||||||
|
echo '<p><strong>Période :</strong> ' . $duration . '</p>';
|
||||||
|
|
||||||
|
if (!empty($tags)) {
|
||||||
|
echo '<div class="project-tags">';
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
echo '<span class="tag">' . htmlspecialchars($tag) . '</span>';
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<p>' . $description . '</p>';
|
||||||
|
|
||||||
|
// Boutons de modification / suppression pour le panel admin
|
||||||
|
if ($admin) {
|
||||||
|
echo '<div class="admin-actions">';
|
||||||
|
echo '<a class="btn-submit btn-edit" href="./admin.php?edit_school=' . $id . '">Modifier</a>';
|
||||||
|
echo '<a class="btn-submit btn-delete" href="../scripts/removeSchool.php?id=' . $id . '" onclick="return confirm(\'Voulez-vous vraiment supprimer cette école ?\');">Supprimer</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
echo '</article>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction qui va afficher les écoles sur la page publique
|
||||||
|
function afficherEcoles() {
|
||||||
|
$ecoles = listeEcoles();
|
||||||
|
|
||||||
|
// Si la table est vide ou n'existe pas encore on n'affiche rien
|
||||||
|
if (empty($ecoles)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="projects-list">';
|
||||||
|
foreach ($ecoles as $ecole) {
|
||||||
|
afficherEcoleCard($ecole, false);
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction identique à celle en haut mais ajout de boutons pour le panel admin
|
||||||
|
function afficherEcolesAdmin() {
|
||||||
|
$ecoles = listeEcoles();
|
||||||
|
|
||||||
|
echo '<div class="projects-list">';
|
||||||
|
foreach ($ecoles as $ecole) {
|
||||||
|
afficherEcoleCard($ecole, true);
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Ajout de connexion a la bdd
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Fonction qui récupère un réglage dans la table settings (clé / valeur)
|
||||||
|
// Retourne null si le réglage n'existe pas OU si la table n'a pas encore été créée :
|
||||||
|
// contrairement aux autres fonctions on ne fait pas de die() ici pour que le site
|
||||||
|
// continue de fonctionner tant que le script SQL n'a pas été exécuté
|
||||||
|
function recupererSetting($name) {
|
||||||
|
global $dbh;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $dbh->prepare('SELECT value FROM settings WHERE name = :name');
|
||||||
|
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
|
||||||
|
$stmt->execute();
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
return $row ? $row['value'] : null;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction qui affiche la notification de la page d'accueil (si elle est activée)
|
||||||
|
function afficherNotice() {
|
||||||
|
$enabled = recupererSetting('site_notice_enabled');
|
||||||
|
$titre = recupererSetting('site_notice_title');
|
||||||
|
$texte = recupererSetting('site_notice_text');
|
||||||
|
|
||||||
|
// Valeurs par défaut tant que la table settings n'existe pas encore
|
||||||
|
if ($titre === null && $texte === null) {
|
||||||
|
$enabled = '1';
|
||||||
|
$titre = 'Des bugs peuvent encore se produire.';
|
||||||
|
$texte = "Si le CSS ou les images s'affichent mal, pensez à vider le cache de votre navigateur (Ctrl + F5 ou Cmd + Shift + R) pour voir les dernières modifications.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si la notification est désactivée ou vide on n'affiche rien
|
||||||
|
if ($enabled !== '1' || (empty($titre) && empty($texte))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<p class="dev-cache-notice">';
|
||||||
|
if (!empty($titre)) {
|
||||||
|
echo '<strong>' . htmlspecialchars($titre) . '</strong> ';
|
||||||
|
}
|
||||||
|
echo nl2br(htmlspecialchars($texte));
|
||||||
|
echo '</p>';
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Ajout de connexion a la bdd
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Fonction qui va faire une requête SQL afin de collecter toutes les catégories de compétences
|
||||||
|
// Retourne un tableau vide si la table n'existe pas encore (pas de die() pour que la page
|
||||||
|
// Compétences continue de fonctionner tant que le script SQL n'a pas été exécuté)
|
||||||
|
function listeSkills() {
|
||||||
|
global $dbh;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $dbh->prepare('SELECT id, title, icon, items, position FROM skills ORDER BY position ASC, id ASC');
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction qui permet de récupérer une catégorie de compétences par id (pour la modification dans le panel Admin)
|
||||||
|
function recupererSkillParId($id) {
|
||||||
|
global $dbh;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $dbh->prepare('SELECT id, title, icon, items, position FROM skills WHERE id = :id');
|
||||||
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction interne qui affiche une carte de compétences (utilisée par la page publique et le panel admin)
|
||||||
|
// Les items sont stockés un par ligne, au format "classes-icone|Texte" (icône optionnelle)
|
||||||
|
function afficherSkillCard($skill, $admin = false) {
|
||||||
|
$id = (int) $skill['id'];
|
||||||
|
$titre = htmlspecialchars($skill['title']);
|
||||||
|
$icone = htmlspecialchars(!empty($skill['icon']) ? $skill['icon'] : 'fa-solid fa-star');
|
||||||
|
|
||||||
|
echo '<div class="skill-category">';
|
||||||
|
echo '<div class="skill-category-icon"><i class="' . $icone . '"></i></div>';
|
||||||
|
echo '<h2>' . $titre . '</h2>';
|
||||||
|
echo '<ul class="skill-list">';
|
||||||
|
|
||||||
|
// Un item par ligne
|
||||||
|
$items = array_filter(array_map('trim', preg_split('/\r\n|\r|\n/', $skill['items'])));
|
||||||
|
foreach ($items as $item) {
|
||||||
|
// Icône optionnelle avant le | sinon icône par défaut
|
||||||
|
$itemIcone = 'fa-solid fa-check';
|
||||||
|
$label = $item;
|
||||||
|
if (strpos($item, '|') !== false) {
|
||||||
|
list($itemIcone, $label) = array_map('trim', explode('|', $item, 2));
|
||||||
|
}
|
||||||
|
echo '<li><i class="' . htmlspecialchars($itemIcone) . '"></i> ' . htmlspecialchars($label) . '</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</ul>';
|
||||||
|
|
||||||
|
// Boutons de modification / suppression pour le panel admin
|
||||||
|
if ($admin) {
|
||||||
|
echo '<div class="admin-actions">';
|
||||||
|
echo '<a class="btn-submit btn-edit" href="./admin.php?edit_skill=' . $id . '">Modifier</a>';
|
||||||
|
echo '<a class="btn-submit btn-delete" href="../scripts/removeSkill.php?id=' . $id . '" onclick="return confirm(\'Voulez-vous vraiment supprimer cette catégorie de compétences ?\');">Supprimer</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction qui va afficher les compétences sur la page publique
|
||||||
|
function afficherSkills() {
|
||||||
|
$skills = listeSkills();
|
||||||
|
|
||||||
|
// Si la table est vide ou n'existe pas encore
|
||||||
|
if (empty($skills)) {
|
||||||
|
echo '<p class="admin-notice">Les compétences seront bientôt disponibles.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="skills-grid">';
|
||||||
|
foreach ($skills as $skill) {
|
||||||
|
afficherSkillCard($skill, false);
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction identique à celle en haut mais ajout de boutons pour le panel admin
|
||||||
|
function afficherSkillsAdmin() {
|
||||||
|
$skills = listeSkills();
|
||||||
|
|
||||||
|
echo '<div class="skills-grid">';
|
||||||
|
foreach ($skills as $skill) {
|
||||||
|
afficherSkillCard($skill, true);
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
+123
-2
@@ -10,6 +10,9 @@ require_once '../functions/databaseConnection.php';
|
|||||||
require_once("../functions/listUser.php");
|
require_once("../functions/listUser.php");
|
||||||
require_once('../functions/listProject.php');
|
require_once('../functions/listProject.php');
|
||||||
require_once('../functions/listXP.php');
|
require_once('../functions/listXP.php');
|
||||||
|
require_once('../functions/listSchool.php');
|
||||||
|
require_once('../functions/listSkills.php');
|
||||||
|
require_once('../functions/listSettings.php');
|
||||||
|
|
||||||
|
|
||||||
// L'utilisateur doit être administrateur
|
// L'utilisateur doit être administrateur
|
||||||
@@ -21,15 +24,28 @@ if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
|||||||
// On met les variables à null ici pour éviter qu'il garde les options en premier chargement
|
// On met les variables à null ici pour éviter qu'il garde les options en premier chargement
|
||||||
$projetAModifier = null;
|
$projetAModifier = null;
|
||||||
$xpAModifier = null;
|
$xpAModifier = null;
|
||||||
|
$ecoleAModifier = null;
|
||||||
|
$skillAModifier = null;
|
||||||
|
|
||||||
|
|
||||||
// Si on modifie XP ou Projets
|
// Si on modifie XP, Projets, École ou Compétence
|
||||||
if (isset($_GET['edit_project']) && is_numeric($_GET['edit_project'])) {
|
if (isset($_GET['edit_project']) && is_numeric($_GET['edit_project'])) {
|
||||||
$projetAModifier = recupererProjetParId((int) $_GET['edit_project']);
|
$projetAModifier = recupererProjetParId((int) $_GET['edit_project']);
|
||||||
}
|
}
|
||||||
if (isset($_GET['edit_xp']) && is_numeric($_GET['edit_xp'])) {
|
if (isset($_GET['edit_xp']) && is_numeric($_GET['edit_xp'])) {
|
||||||
$xpAModifier = recupererXPParId((int) $_GET['edit_xp']);
|
$xpAModifier = recupererXPParId((int) $_GET['edit_xp']);
|
||||||
}
|
}
|
||||||
|
if (isset($_GET['edit_school']) && is_numeric($_GET['edit_school'])) {
|
||||||
|
$ecoleAModifier = recupererEcoleParId((int) $_GET['edit_school']);
|
||||||
|
}
|
||||||
|
if (isset($_GET['edit_skill']) && is_numeric($_GET['edit_skill'])) {
|
||||||
|
$skillAModifier = recupererSkillParId((int) $_GET['edit_skill']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valeurs actuelles de la notification de la page d'accueil
|
||||||
|
$noticeTitle = recupererSetting('site_notice_title') ?? '';
|
||||||
|
$noticeText = recupererSetting('site_notice_text') ?? '';
|
||||||
|
$noticeEnabled = recupererSetting('site_notice_enabled') ?? '1';
|
||||||
|
|
||||||
// Page du panneau d'administration
|
// Page du panneau d'administration
|
||||||
?>
|
?>
|
||||||
@@ -108,6 +124,104 @@ if (isset($_GET['edit_xp']) && is_numeric($_GET['edit_xp'])) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Formulaire de création d'école / formation -->
|
||||||
|
<div class="project-container">
|
||||||
|
<div class="project-form-card">
|
||||||
|
|
||||||
|
<div class="project-header">
|
||||||
|
<h2><?php echo $ecoleAModifier ? 'Modifier l\'école' : 'Nouvelle école'; ?></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- on envoie les données à addSchool.php -->
|
||||||
|
<form method="POST" action="<?php echo $ecoleAModifier ? '../scripts/updateSchool.php' : '../scripts/addSchool.php'; ?>">
|
||||||
|
|
||||||
|
<?php if ($ecoleAModifier): ?>
|
||||||
|
<input type="hidden" name="id" value="<?php echo (int) $ecoleAModifier['id']; ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<label>Nom de l'école *</label>
|
||||||
|
<input type="text" name="title" required value="<?php echo $ecoleAModifier ? htmlspecialchars($ecoleAModifier['title']) : ''; ?>">
|
||||||
|
|
||||||
|
<label>Tags (virgules)</label>
|
||||||
|
<input type="text" name="tags" value="<?php echo $ecoleAModifier ? htmlspecialchars($ecoleAModifier['tags']) : ''; ?>">
|
||||||
|
|
||||||
|
<label>Description (diplôme, spécialité...) *</label>
|
||||||
|
<textarea name="description" required><?php echo $ecoleAModifier ? htmlspecialchars($ecoleAModifier['description']) : ''; ?></textarea>
|
||||||
|
|
||||||
|
<label>Période (ex : 2023 - 2025) *</label>
|
||||||
|
<input type="text" name="duration" required value="<?php echo $ecoleAModifier ? htmlspecialchars($ecoleAModifier['duration']) : ''; ?>">
|
||||||
|
|
||||||
|
<label>Lien de l'image *</label>
|
||||||
|
<input type="url" name="imageurl" placeholder="https://..." required value="<?php echo $ecoleAModifier ? htmlspecialchars($ecoleAModifier['image_url']) : ''; ?>">
|
||||||
|
|
||||||
|
<input type="submit" name="submitschool" value="<?php echo $ecoleAModifier ? 'Modifier l\'école' : '+ Ajouter l\'école'; ?>" class="btn-submit">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Formulaire de création de catégorie de compétences -->
|
||||||
|
<div class="project-container">
|
||||||
|
<div class="project-form-card">
|
||||||
|
|
||||||
|
<div class="project-header">
|
||||||
|
<h2><?php echo $skillAModifier ? 'Modifier la compétence' : 'Nouvelle compétence'; ?></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- on envoie les données à addSkill.php -->
|
||||||
|
<form method="POST" action="<?php echo $skillAModifier ? '../scripts/updateSkill.php' : '../scripts/addSkill.php'; ?>">
|
||||||
|
|
||||||
|
<?php if ($skillAModifier): ?>
|
||||||
|
<input type="hidden" name="id" value="<?php echo (int) $skillAModifier['id']; ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<label>Titre de la catégorie *</label>
|
||||||
|
<input type="text" name="title" required value="<?php echo $skillAModifier ? htmlspecialchars($skillAModifier['title']) : ''; ?>">
|
||||||
|
|
||||||
|
<label>Icône de la catégorie (classes Font Awesome, ex : fa-solid fa-terminal)</label>
|
||||||
|
<input type="text" name="icon" placeholder="fa-solid fa-star" value="<?php echo $skillAModifier ? htmlspecialchars($skillAModifier['icon']) : ''; ?>">
|
||||||
|
|
||||||
|
<label>Compétences (une par ligne, format : "icone|texte" ou juste "texte") *</label>
|
||||||
|
<textarea name="items" placeholder="fa-brands fa-python|Python fa-solid fa-terminal|Bash Ansible" required><?php echo $skillAModifier ? htmlspecialchars($skillAModifier['items']) : ''; ?></textarea>
|
||||||
|
|
||||||
|
<label>Position (ordre d'affichage, du plus petit au plus grand)</label>
|
||||||
|
<input type="text" name="position" value="<?php echo $skillAModifier ? (int) $skillAModifier['position'] : '0'; ?>">
|
||||||
|
|
||||||
|
<input type="submit" name="submitskill" value="<?php echo $skillAModifier ? 'Modifier la compétence' : '+ Ajouter la compétence'; ?>" class="btn-submit">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Formulaire de la notification de la page d'accueil -->
|
||||||
|
<div class="project-container">
|
||||||
|
<div class="project-form-card">
|
||||||
|
|
||||||
|
<div class="project-header">
|
||||||
|
<h2>Notification de la page d'accueil</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- on envoie les données à updateSettings.php -->
|
||||||
|
<form method="POST" action="../scripts/updateSettings.php">
|
||||||
|
|
||||||
|
<label>Titre (affiché en gras)</label>
|
||||||
|
<input type="text" name="notice_title" value="<?php echo htmlspecialchars($noticeTitle); ?>">
|
||||||
|
|
||||||
|
<label>Texte</label>
|
||||||
|
<textarea name="notice_text"><?php echo htmlspecialchars($noticeText); ?></textarea>
|
||||||
|
|
||||||
|
<!-- Checkbox pour activer / désactiver la notification sans la supprimer -->
|
||||||
|
<label class="checkbox-row">
|
||||||
|
<input type="checkbox" name="notice_enabled" value="1" <?php echo $noticeEnabled === '1' ? 'checked' : ''; ?>>
|
||||||
|
Afficher la notification sur la page d'accueil
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input type="submit" name="submitsettings" value="Enregistrer la notification" class="btn-submit">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
@@ -116,7 +230,14 @@ if (isset($_GET['edit_xp']) && is_numeric($_GET['edit_xp'])) {
|
|||||||
<h2 class="projects-title">Gérer les projets</h2>
|
<h2 class="projects-title">Gérer les projets</h2>
|
||||||
<?php afficherProjetsAdmin(); ?>
|
<?php afficherProjetsAdmin(); ?>
|
||||||
<h2 class="projects-title">Gérer les experiences</h2>
|
<h2 class="projects-title">Gérer les experiences</h2>
|
||||||
<?php afficherXPAdmin();
|
<?php afficherXPAdmin(); ?>
|
||||||
|
<h2 class="projects-title">Gérer les écoles</h2>
|
||||||
|
<?php afficherEcolesAdmin(); ?>
|
||||||
|
<h2 class="projects-title">Gérer les compétences</h2>
|
||||||
|
<section class="skills-section skills-section-admin">
|
||||||
|
<?php afficherSkillsAdmin(); ?>
|
||||||
|
</section>
|
||||||
|
<?php
|
||||||
|
|
||||||
// Création de compte
|
// Création de compte
|
||||||
?>
|
?>
|
||||||
|
|||||||
+4
-9
@@ -9,17 +9,12 @@ $pageTitle = 'Accueil';
|
|||||||
// Insertion du header
|
// Insertion du header
|
||||||
require_once('./../elements/header.php');
|
require_once('./../elements/header.php');
|
||||||
|
|
||||||
// Ajout d'un texte pour rappeller aux utilisateurs de la démo de vider le cache si du CSS s'affiche mal
|
// La notification est maintenant dynamique : modifiable depuis le panel Admin (table settings)
|
||||||
|
require_once('../functions/listSettings.php');
|
||||||
//Si du CSS / ou des images s'affichent mal, il faut penser à vider le cache, version de développement uniquement.
|
|
||||||
|
|
||||||
|
// Affichage de la notification (si elle est activée dans le panel Admin)
|
||||||
|
afficherNotice();
|
||||||
?>
|
?>
|
||||||
<!-- Message d'alerte pour les utilisateurs de la démo -->
|
|
||||||
<p class="dev-cache-notice">
|
|
||||||
<strong>Version de développement uniquement.</strong>
|
|
||||||
Si le CSS ou les images s'affichent mal, pensez à vider le cache de votre navigateur
|
|
||||||
(Ctrl + F5 ou Cmd + Shift + R) pour voir les dernières modifications.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- Section hero : présentation rapide -->
|
<!-- Section hero : présentation rapide -->
|
||||||
<section class="hero">
|
<section class="hero">
|
||||||
|
|||||||
+11
-90
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Compétences
|
// Compétences : le contenu est maintenant dynamique, géré depuis le panel Admin (table skills)
|
||||||
|
|
||||||
// Titre de l'onglet
|
// Titre de l'onglet
|
||||||
$pageTitle = 'Compétences';
|
$pageTitle = 'Compétences';
|
||||||
@@ -8,6 +8,11 @@ $pageTitle = 'Compétences';
|
|||||||
// On ajoute ici le header
|
// On ajoute ici le header
|
||||||
require_once('./../elements/header.php');
|
require_once('./../elements/header.php');
|
||||||
|
|
||||||
|
// Ajout d'un message indiquant si l'utilisateur est admin qu'il peut modifier ces compétences
|
||||||
|
if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {
|
||||||
|
echo "<p class='admin-notice'>Vous êtes administrateur, vous pouvez modifier les compétences <a href='../pages/admin.php'>ici</a>.</p>";
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!-- Contenu de la page skills -->
|
<!-- Contenu de la page skills -->
|
||||||
@@ -18,96 +23,12 @@ require_once('./../elements/header.php');
|
|||||||
<p>Un aperçu des technologies et des domaines que je maîtrise.</p>
|
<p>Un aperçu des technologies et des domaines que je maîtrise.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="skills-grid">
|
<?php
|
||||||
|
require_once('../functions/listSkills.php');
|
||||||
|
|
||||||
<!-- Systèmes d'exploitation -->
|
// Appel de ma fonction dans Functions/listSkills.php
|
||||||
<div class="skill-category">
|
afficherSkills();
|
||||||
<div class="skill-category-icon">
|
?>
|
||||||
<i class="fa-solid fa-computer"></i>
|
|
||||||
</div>
|
|
||||||
<h2>Systèmes d'exploitation</h2>
|
|
||||||
<ul class="skill-list">
|
|
||||||
<li><i class="fa-brands fa-microsoft"></i> Windows (Serveur/XP/8/10/11)</li>
|
|
||||||
<li><i class="fa-brands fa-linux"></i> Linux (Debian/Ubuntu)</li>
|
|
||||||
<li><i class="fa-brands fa-apple"></i> macOS</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Réseaux -->
|
|
||||||
<div class="skill-category">
|
|
||||||
<div class="skill-category-icon">
|
|
||||||
<i class="fa-solid fa-network-wired"></i>
|
|
||||||
</div>
|
|
||||||
<h2>Réseaux</h2>
|
|
||||||
<ul class="skill-list">
|
|
||||||
<li><i class="fa-solid fa-location-dot"></i> Adressage IP</li>
|
|
||||||
<li><i class="fa-solid fa-diagram-project"></i> VLAN</li>
|
|
||||||
<li><i class="fa-solid fa-shield-halved"></i> Pare-feu (iptables/ufw)</li>
|
|
||||||
<li><i class="fa-solid fa-globe"></i> DNS (Bind9)</li>
|
|
||||||
<li><i class="fa-solid fa-server"></i> DHCP (ISC DHCP)</li>
|
|
||||||
<li><i class="fa-solid fa-arrows-left-right"></i> Proxy (Squid)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scripting -->
|
|
||||||
<div class="skill-category">
|
|
||||||
<div class="skill-category-icon">
|
|
||||||
<i class="fa-solid fa-terminal"></i>
|
|
||||||
</div>
|
|
||||||
<h2>Scripting</h2>
|
|
||||||
<ul class="skill-list">
|
|
||||||
<li><i class="fa-solid fa-terminal"></i> Bash</li>
|
|
||||||
<li><i class="fa-brands fa-python"></i> Python</li>
|
|
||||||
<li><i class="fa-brands fa-java"></i> Java</li>
|
|
||||||
<li><i class="fa-solid fa-gears"></i> Ansible</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Applications -->
|
|
||||||
<div class="skill-category">
|
|
||||||
<div class="skill-category-icon">
|
|
||||||
<i class="fa-solid fa-cubes"></i>
|
|
||||||
</div>
|
|
||||||
<h2>Applications</h2>
|
|
||||||
<ul class="skill-list">
|
|
||||||
<li><i class="fa-solid fa-address-book"></i> LDAP (Windows AD)</li>
|
|
||||||
<li><i class="fa-solid fa-headset"></i> GLPI</li>
|
|
||||||
<li><i class="fa-solid fa-compact-disc"></i> FOG</li>
|
|
||||||
<li><i class="fa-solid fa-server"></i> Nginx / Apache</li>
|
|
||||||
<li><i class="fa-brands fa-docker"></i> Docker</li>
|
|
||||||
<li><i class="fa-solid fa-display"></i> Guacamole</li>
|
|
||||||
<li><i class="fa-solid fa-database"></i> MySQL / MariaDB</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Développement web -->
|
|
||||||
<div class="skill-category">
|
|
||||||
<div class="skill-category-icon">
|
|
||||||
<i class="fa-solid fa-code"></i>
|
|
||||||
</div>
|
|
||||||
<h2>Développement web</h2>
|
|
||||||
<ul class="skill-list">
|
|
||||||
<li><i class="fa-brands fa-html5"></i> HTML</li>
|
|
||||||
<li><i class="fa-brands fa-css3-alt"></i> CSS</li>
|
|
||||||
<li><i class="fa-brands fa-js"></i> JavaScript</li>
|
|
||||||
<li><i class="fa-brands fa-php"></i> PHP</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Virtualisation & Outils -->
|
|
||||||
<div class="skill-category">
|
|
||||||
<div class="skill-category-icon">
|
|
||||||
<i class="fa-solid fa-layer-group"></i>
|
|
||||||
</div>
|
|
||||||
<h2>Virtualisation & Outils</h2>
|
|
||||||
<ul class="skill-list">
|
|
||||||
<li><i class="fa-solid fa-server"></i> Proxmox</li>
|
|
||||||
<li><i class="fa-solid fa-box"></i> VirtualBox</li>
|
|
||||||
<li><i class="fa-brands fa-git-alt"></i> Git (GitHub, Gitea)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,22 @@ require_once('../functions/listXP.php');
|
|||||||
|
|
||||||
// Appel de ma fonction dans Functions/listXP.php
|
// Appel de ma fonction dans Functions/listXP.php
|
||||||
afficherXP();
|
afficherXP();
|
||||||
|
require_once('../functions/listSchool.php');
|
||||||
|
|
||||||
|
// Section écoles / formations, en dessous des expériences professionnelles
|
||||||
|
// On n'affiche le titre que s'il y a au moins une école (table remplie)
|
||||||
|
if (!empty(listeEcoles())) {
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>Mes écoles</h2>
|
||||||
|
<p>Mon parcours scolaire et mes formations.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Appel de ma fonction dans Functions/listSchool.php
|
||||||
|
afficherEcoles();
|
||||||
|
}
|
||||||
|
|
||||||
// Ajout du footer
|
// Ajout du footer
|
||||||
require_once('../elements/footer.php'); ?>
|
require_once('../elements/footer.php'); ?>
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once '../functions/databaseConnection.php';
|
||||||
|
|
||||||
|
// Pour faire cette fonction l'utilisateur doit être administrateur
|
||||||
|
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||||
|
header('Location: ../pages/home.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// La requête doit être un POST sinon on l'a refuse
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
// Récupération des données dans la requete POST
|
||||||
|
$title = trim($_POST['title'] ?? '');
|
||||||
|
$tags = trim($_POST['tags'] ?? '');
|
||||||
|
$description = trim($_POST['description'] ?? '');
|
||||||
|
$duration = trim($_POST['duration'] ?? '');
|
||||||
|
$image_url = trim($_POST['imageurl'] ?? '');
|
||||||
|
|
||||||
|
// On vérifie que les champs obligatoires ne sont pas vide
|
||||||
|
if (!empty($title) && !empty($description) && !empty($duration) && !empty($image_url)) {
|
||||||
|
try {
|
||||||
|
// On prépare la requête SQL pour insérer la nouvelle école
|
||||||
|
$stmt = $dbh->prepare('INSERT INTO schools (title, tags, description, duration, image_url) VALUES (:title, :tags, :description, :duration, :image_url)');
|
||||||
|
|
||||||
|
// On execute la requete préparée
|
||||||
|
$stmt->execute([
|
||||||
|
':title' => $title,
|
||||||
|
':tags' => $tags,
|
||||||
|
':description' => $description,
|
||||||
|
':duration' => $duration,
|
||||||
|
':image_url' => $image_url
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Redirection vers xp.php dès que la requete est executée
|
||||||
|
header('Location: ../pages/xp.php');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// En cas d'erreur :
|
||||||
|
echo "Erreur pendant l'ajout de l'école: " . $e->getMessage();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Veuillez remplir tous les champs obligatoires.";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Si on y accède de façon peu légitime sans requête POST on refuse
|
||||||
|
header('Location: ../pages/xp.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once '../functions/databaseConnection.php';
|
||||||
|
|
||||||
|
// Pour faire cette fonction l'utilisateur doit être administrateur
|
||||||
|
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||||
|
header('Location: ../pages/home.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// La requête doit être un POST sinon on l'a refuse
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
// Récupération des données dans la requete POST
|
||||||
|
$title = trim($_POST['title'] ?? '');
|
||||||
|
$icon = trim($_POST['icon'] ?? '');
|
||||||
|
$items = trim($_POST['items'] ?? '');
|
||||||
|
$position = (int) ($_POST['position'] ?? 0);
|
||||||
|
|
||||||
|
// On vérifie que les champs obligatoires ne sont pas vide
|
||||||
|
if (!empty($title) && !empty($items)) {
|
||||||
|
try {
|
||||||
|
// On prépare la requête SQL pour insérer la nouvelle catégorie de compétences
|
||||||
|
$stmt = $dbh->prepare('INSERT INTO skills (title, icon, items, position) VALUES (:title, :icon, :items, :position)');
|
||||||
|
|
||||||
|
// On execute la requete préparée
|
||||||
|
$stmt->execute([
|
||||||
|
':title' => $title,
|
||||||
|
':icon' => $icon,
|
||||||
|
':items' => $items,
|
||||||
|
':position' => $position
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Redirection vers skills.php dès que la requete est executée
|
||||||
|
header('Location: ../pages/skills.php');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// En cas d'erreur :
|
||||||
|
echo "Erreur pendant l'ajout de la compétence: " . $e->getMessage();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Veuillez remplir tous les champs obligatoires.";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Si on y accède de façon peu légitime sans requête POST on refuse
|
||||||
|
header('Location: ../pages/skills.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
// On démarre la session
|
||||||
|
session_start();
|
||||||
|
// On ajoute la DB
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Si l'utilisateur n'est pas admin il ne peut pas executer le reste du script
|
||||||
|
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||||
|
header('Location: ../pages/home.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
//L'id doit être collecté sinon l'utilisateur ne peux pas executer le reste du script
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On collecte l'id et on le met en variable locale
|
||||||
|
$id = (int) $_GET['id'];
|
||||||
|
|
||||||
|
try { // On essaie d'executer une requête qui est préparée
|
||||||
|
$stmt = $dbh->prepare('DELETE FROM schools WHERE id = :id');
|
||||||
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute(); // Sinon on renvoie l'erreur
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Erreur lors de la suppression de l'école :" . $e->getMessage());
|
||||||
|
}
|
||||||
|
// On renvoie l'utilisateur sur admin.php
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
// On démarre la session
|
||||||
|
session_start();
|
||||||
|
// On ajoute la DB
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Si l'utilisateur n'est pas admin il ne peut pas executer le reste du script
|
||||||
|
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||||
|
header('Location: ../pages/home.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
//L'id doit être collecté sinon l'utilisateur ne peux pas executer le reste du script
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On collecte l'id et on le met en variable locale
|
||||||
|
$id = (int) $_GET['id'];
|
||||||
|
|
||||||
|
try { // On essaie d'executer une requête qui est préparée
|
||||||
|
$stmt = $dbh->prepare('DELETE FROM skills WHERE id = :id');
|
||||||
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute(); // Sinon on renvoie l'erreur
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Erreur lors de la suppression de la compétence :" . $e->getMessage());
|
||||||
|
}
|
||||||
|
// On renvoie l'utilisateur sur admin.php
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
// On démarre la session
|
||||||
|
session_start();
|
||||||
|
// On ajoute la DB
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Si l'utilisateur n'est pas admin il ne peut pas executer le reste du script
|
||||||
|
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||||
|
header('Location: ../pages/home.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( // Si les champs sont vides on renvoie vers admin.php
|
||||||
|
!isset($_POST['id']) || !is_numeric($_POST['id']) ||
|
||||||
|
empty($_POST['title']) ||
|
||||||
|
empty($_POST['description']) ||
|
||||||
|
empty($_POST['duration']) ||
|
||||||
|
empty($_POST['imageurl'])
|
||||||
|
) {
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On récupère les données pour les mettres en variables locales
|
||||||
|
$id = (int) $_POST['id'];
|
||||||
|
$title = trim($_POST['title']);
|
||||||
|
$tags = trim($_POST['tags'] ?? '');
|
||||||
|
$description = trim($_POST['description']);
|
||||||
|
$duration = trim($_POST['duration']);
|
||||||
|
$imageurl = trim($_POST['imageurl']);
|
||||||
|
|
||||||
|
try { // On essaie d'executer une requête qui est préparée
|
||||||
|
$stmt = $dbh->prepare('
|
||||||
|
UPDATE schools
|
||||||
|
SET title = :title, tags = :tags, description = :description, duration = :duration, image_url = :image_url
|
||||||
|
WHERE id = :id
|
||||||
|
');
|
||||||
|
$stmt->bindValue(':title', $title, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':tags', $tags, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':description', $description, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':duration', $duration, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':image_url', $imageurl, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute(); // Sinon on renvoie l'erreur
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Erreur lors de la modification de l'école :" . $e->getMessage());
|
||||||
|
}
|
||||||
|
// On renvoie l'utilisateur sur admin.php
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
// On démarre la session
|
||||||
|
session_start();
|
||||||
|
// On ajoute la DB
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Si l'utilisateur n'est pas admin il ne peut pas executer le reste du script
|
||||||
|
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||||
|
header('Location: ../pages/home.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// La requête doit être un POST sinon on l'a refuse
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On récupère les données pour les mettres en variables locales
|
||||||
|
// La checkbox n'est envoyée que si elle est cochée
|
||||||
|
$reglages = [
|
||||||
|
'site_notice_title' => trim($_POST['notice_title'] ?? ''),
|
||||||
|
'site_notice_text' => trim($_POST['notice_text'] ?? ''),
|
||||||
|
'site_notice_enabled' => isset($_POST['notice_enabled']) ? '1' : '0'
|
||||||
|
];
|
||||||
|
|
||||||
|
try { // On essaie d'executer une requête qui est préparée pour chaque réglage
|
||||||
|
// ON DUPLICATE KEY UPDATE : crée le réglage s'il n'existe pas encore, le met à jour sinon
|
||||||
|
$stmt = $dbh->prepare('INSERT INTO settings (name, value) VALUES (:name, :value) ON DUPLICATE KEY UPDATE value = VALUES(value)');
|
||||||
|
|
||||||
|
foreach ($reglages as $name => $value) {
|
||||||
|
$stmt->execute([
|
||||||
|
':name' => $name,
|
||||||
|
':value' => $value
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Erreur lors de la modification de la notification :" . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// On renvoie l'utilisateur sur admin.php
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
// On démarre la session
|
||||||
|
session_start();
|
||||||
|
// On ajoute la DB
|
||||||
|
require_once('../functions/databaseConnection.php');
|
||||||
|
|
||||||
|
// Si l'utilisateur n'est pas admin il ne peut pas executer le reste du script
|
||||||
|
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||||
|
header('Location: ../pages/home.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( // Si les champs sont vides on renvoie vers admin.php
|
||||||
|
!isset($_POST['id']) || !is_numeric($_POST['id']) ||
|
||||||
|
empty($_POST['title']) ||
|
||||||
|
empty($_POST['items'])
|
||||||
|
) {
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On récupère les données pour les mettres en variables locales
|
||||||
|
$id = (int) $_POST['id'];
|
||||||
|
$title = trim($_POST['title']);
|
||||||
|
$icon = trim($_POST['icon'] ?? '');
|
||||||
|
$items = trim($_POST['items']);
|
||||||
|
$position = (int) ($_POST['position'] ?? 0);
|
||||||
|
|
||||||
|
try { // On essaie d'executer une requête qui est préparée
|
||||||
|
$stmt = $dbh->prepare('
|
||||||
|
UPDATE skills
|
||||||
|
SET title = :title, icon = :icon, items = :items, position = :position
|
||||||
|
WHERE id = :id
|
||||||
|
');
|
||||||
|
$stmt->bindValue(':title', $title, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':icon', $icon, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':items', $items, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(':position', $position, PDO::PARAM_INT);
|
||||||
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute(); // Sinon on renvoie l'erreur
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Erreur lors de la modification de la compétence :" . $e->getMessage());
|
||||||
|
}
|
||||||
|
// On renvoie l'utilisateur sur admin.php
|
||||||
|
header('Location: ../pages/admin.php');
|
||||||
|
exit;
|
||||||
+270
-33
@@ -9,11 +9,11 @@
|
|||||||
--surface-3: #202030;
|
--surface-3: #202030;
|
||||||
--border: #262638;
|
--border: #262638;
|
||||||
--border-strong: #34344a;
|
--border-strong: #34344a;
|
||||||
--accent: #6c63ff;
|
--accent: #ff7a1a;
|
||||||
--accent-strong: #574fe0;
|
--accent-strong: #e8630a;
|
||||||
--accent-light: #8f88ff;
|
--accent-light: #ffa155;
|
||||||
--accent-soft: rgba(108, 99, 255, 0.14);
|
--accent-soft: rgba(255, 122, 26, 0.14);
|
||||||
--accent-border: rgba(108, 99, 255, 0.32);
|
--accent-border: rgba(255, 122, 26, 0.32);
|
||||||
--text: #f2f2f8;
|
--text: #f2f2f8;
|
||||||
--text-soft: #c7c7d6;
|
--text-soft: #c7c7d6;
|
||||||
--text-muted: #9b9bb2;
|
--text-muted: #9b9bb2;
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
--radius-lg: 22px;
|
--radius-lg: 22px;
|
||||||
--shadow-sm: 0 4px 14px rgba(0, 0, 0, 0.25);
|
--shadow-sm: 0 4px 14px rgba(0, 0, 0, 0.25);
|
||||||
--shadow: 0 14px 34px rgba(0, 0, 0, 0.35);
|
--shadow: 0 14px 34px rgba(0, 0, 0, 0.35);
|
||||||
--shadow-accent: 0 14px 34px rgba(108, 99, 255, 0.22);
|
--shadow-accent: 0 14px 34px rgba(255, 122, 26, 0.22);
|
||||||
--glow-1: rgba(108, 99, 255, 0.14);
|
--glow-1: rgba(255, 122, 26, 0.14);
|
||||||
--glow-2: rgba(108, 99, 255, 0.07);
|
--glow-2: rgba(255, 122, 26, 0.07);
|
||||||
--header-bg: rgba(12, 12, 18, 0.78);
|
--header-bg: rgba(12, 12, 18, 0.78);
|
||||||
--nav-bg: rgba(12, 12, 18, 0.97);
|
--nav-bg: rgba(12, 12, 18, 0.97);
|
||||||
--footer-bg: rgba(12, 12, 18, 0.6);
|
--footer-bg: rgba(12, 12, 18, 0.6);
|
||||||
@@ -44,17 +44,17 @@
|
|||||||
--surface-3: #e9e9f2;
|
--surface-3: #e9e9f2;
|
||||||
--border: #e3e3ee;
|
--border: #e3e3ee;
|
||||||
--border-strong: #cfcfe0;
|
--border-strong: #cfcfe0;
|
||||||
--accent-light: #5a51e8;
|
--accent-light: #d95d00;
|
||||||
--accent-soft: rgba(108, 99, 255, 0.1);
|
--accent-soft: rgba(255, 122, 26, 0.1);
|
||||||
--accent-border: rgba(108, 99, 255, 0.3);
|
--accent-border: rgba(255, 122, 26, 0.3);
|
||||||
--text: #191924;
|
--text: #191924;
|
||||||
--text-soft: #3c3c4e;
|
--text-soft: #3c3c4e;
|
||||||
--text-muted: #6d6d84;
|
--text-muted: #6d6d84;
|
||||||
--shadow-sm: 0 4px 14px rgba(30, 30, 60, 0.08);
|
--shadow-sm: 0 4px 14px rgba(30, 30, 60, 0.08);
|
||||||
--shadow: 0 14px 34px rgba(30, 30, 60, 0.12);
|
--shadow: 0 14px 34px rgba(30, 30, 60, 0.12);
|
||||||
--shadow-accent: 0 14px 34px rgba(108, 99, 255, 0.18);
|
--shadow-accent: 0 14px 34px rgba(255, 122, 26, 0.18);
|
||||||
--glow-1: rgba(108, 99, 255, 0.1);
|
--glow-1: rgba(255, 122, 26, 0.1);
|
||||||
--glow-2: rgba(108, 99, 255, 0.05);
|
--glow-2: rgba(255, 122, 26, 0.05);
|
||||||
--header-bg: rgba(255, 255, 255, 0.82);
|
--header-bg: rgba(255, 255, 255, 0.82);
|
||||||
--nav-bg: rgba(255, 255, 255, 0.98);
|
--nav-bg: rgba(255, 255, 255, 0.98);
|
||||||
--footer-bg: rgba(255, 255, 255, 0.7);
|
--footer-bg: rgba(255, 255, 255, 0.7);
|
||||||
@@ -181,7 +181,7 @@ img {
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
color: white;
|
color: white;
|
||||||
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-strong) 100%);
|
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-strong) 100%);
|
||||||
box-shadow: 0 6px 18px rgba(108, 99, 255, 0.3);
|
box-shadow: 0 6px 18px rgba(255, 122, 26, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-nav a.nav-cta:hover {
|
.site-nav a.nav-cta:hover {
|
||||||
@@ -306,15 +306,15 @@ img {
|
|||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
width: calc(100% - 32px);
|
width: calc(100% - 32px);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
background: linear-gradient(135deg, rgba(255, 196, 0, 0.14), rgba(255, 140, 0, 0.08));
|
background: linear-gradient(135deg, rgba(108, 99, 255, 0.16), rgba(87, 79, 224, 0.1));
|
||||||
border: 1px solid rgba(255, 196, 0, 0.3);
|
border: 1px solid rgba(108, 99, 255, 0.35);
|
||||||
color: #ffeeba;
|
color: #dcd9ff;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dev-cache-notice strong {
|
.dev-cache-notice strong {
|
||||||
color: #ffd86b;
|
color: #b7b1ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dev-cache-notice::before {
|
.dev-cache-notice::before {
|
||||||
@@ -325,13 +325,13 @@ img {
|
|||||||
|
|
||||||
/* Version thème clair du bandeau d'avertissement */
|
/* Version thème clair du bandeau d'avertissement */
|
||||||
[data-theme="light"] .dev-cache-notice {
|
[data-theme="light"] .dev-cache-notice {
|
||||||
background: linear-gradient(135deg, rgba(255, 196, 0, 0.16), rgba(255, 140, 0, 0.1));
|
background: linear-gradient(135deg, rgba(108, 99, 255, 0.12), rgba(87, 79, 224, 0.07));
|
||||||
border-color: rgba(190, 130, 0, 0.35);
|
border-color: rgba(108, 99, 255, 0.35);
|
||||||
color: #7a5b00;
|
color: #4a43c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] .dev-cache-notice strong {
|
[data-theme="light"] .dev-cache-notice strong {
|
||||||
color: #8a6400;
|
color: #3a34a8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Message admin en haut des listes ---- */
|
/* ---- Message admin en haut des listes ---- */
|
||||||
@@ -383,12 +383,75 @@ img {
|
|||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reflet lumineux qui balaye le prénom en continu */
|
||||||
.hero h1 .accent {
|
.hero h1 .accent {
|
||||||
background: linear-gradient(120deg, var(--accent-light), var(--accent));
|
background: linear-gradient(
|
||||||
|
120deg,
|
||||||
|
var(--accent-light) 0%,
|
||||||
|
var(--accent) 35%,
|
||||||
|
#ffd9b3 50%,
|
||||||
|
var(--accent) 65%,
|
||||||
|
var(--accent-light) 100%
|
||||||
|
);
|
||||||
|
background-size: 200% auto;
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
|
animation: heroShimmer 4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apparition en cascade des éléments du hero au chargement */
|
||||||
|
.hero-badge,
|
||||||
|
.hero h1,
|
||||||
|
.hero p,
|
||||||
|
.hero-actions {
|
||||||
|
opacity: 0;
|
||||||
|
animation: heroFadeUp 0.7s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
animation-delay: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-actions {
|
||||||
|
animation-delay: 0.45s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes heroFadeUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(18px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes heroShimmer {
|
||||||
|
to {
|
||||||
|
background-position: -200% center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Désactive les animations si l'utilisateur préfère un affichage sans mouvement */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.hero-badge,
|
||||||
|
.hero h1,
|
||||||
|
.hero p,
|
||||||
|
.hero-actions {
|
||||||
|
animation: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 .accent {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero p {
|
.hero p {
|
||||||
@@ -421,13 +484,13 @@ img {
|
|||||||
.btn-primary {
|
.btn-primary {
|
||||||
color: white;
|
color: white;
|
||||||
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-strong) 100%);
|
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-strong) 100%);
|
||||||
box-shadow: 0 10px 26px rgba(108, 99, 255, 0.32);
|
box-shadow: 0 10px 26px rgba(255, 122, 26, 0.32);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary:hover {
|
.btn-primary:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
filter: brightness(1.08);
|
filter: brightness(1.08);
|
||||||
box-shadow: 0 14px 32px rgba(108, 99, 255, 0.4);
|
box-shadow: 0 14px 32px rgba(255, 122, 26, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-ghost {
|
.btn-ghost {
|
||||||
@@ -449,7 +512,8 @@ img {
|
|||||||
padding: 0 clamp(16px, 4vw, 40px);
|
padding: 0 clamp(16px, 4vw, 40px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header h1 {
|
.page-header h1,
|
||||||
|
.page-header h2 {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
font-size: clamp(28px, 4.5vw, 42px);
|
font-size: clamp(28px, 4.5vw, 42px);
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
@@ -534,7 +598,7 @@ img {
|
|||||||
.login-card input[type="password"]:focus {
|
.login-card input[type="password"]:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.2);
|
box-shadow: 0 0 0 3px rgba(255, 122, 26, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-card input::placeholder {
|
.login-card input::placeholder {
|
||||||
@@ -554,7 +618,7 @@ img {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
box-shadow: 0 10px 24px rgba(108, 99, 255, 0.28);
|
box-shadow: 0 10px 24px rgba(255, 122, 26, 0.28);
|
||||||
transition: transform 0.2s ease, filter 0.2s ease;
|
transition: transform 0.2s ease, filter 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +727,7 @@ img {
|
|||||||
.project-form-card textarea:focus {
|
.project-form-card textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.18);
|
box-shadow: 0 0 0 3px rgba(255, 122, 26, 0.18);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-form-card textarea {
|
.project-form-card textarea {
|
||||||
@@ -700,6 +764,24 @@ img {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Case à cocher (formulaire notification du panel admin) */
|
||||||
|
.checkbox-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-row input[type="checkbox"] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
accent-color: var(--accent);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
/* Bouton violet */
|
/* Bouton violet */
|
||||||
.btn-submit {
|
.btn-submit {
|
||||||
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-strong) 100%);
|
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-strong) 100%);
|
||||||
@@ -935,7 +1017,7 @@ img {
|
|||||||
|
|
||||||
.contact-info {
|
.contact-info {
|
||||||
background:
|
background:
|
||||||
radial-gradient(400px 200px at 20% 0%, rgba(108, 99, 255, 0.16), transparent 70%),
|
radial-gradient(400px 200px at 20% 0%, rgba(255, 122, 26, 0.16), transparent 70%),
|
||||||
var(--surface-2);
|
var(--surface-2);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
@@ -1008,7 +1090,7 @@ img {
|
|||||||
.contact-form textarea:focus {
|
.contact-form textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.18);
|
box-shadow: 0 0 0 3px rgba(255, 122, 26, 0.18);
|
||||||
background-color: var(--surface-3);
|
background-color: var(--surface-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,7 +1106,7 @@ img {
|
|||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: 0 10px 24px rgba(108, 99, 255, 0.28);
|
box-shadow: 0 10px 24px rgba(255, 122, 26, 0.28);
|
||||||
transition: filter 0.2s ease, transform 0.2s ease;
|
transition: filter 0.2s ease, transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1262,6 +1344,17 @@ img {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Boutons admin dans les cartes de compétences */
|
||||||
|
.skill-category .admin-actions {
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grille des compétences dans le panel admin : moins d'espace vertical que la page publique */
|
||||||
|
.skills-section-admin {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- PAGE CGU ---- */
|
/* ---- PAGE CGU ---- */
|
||||||
.cgu-page {
|
.cgu-page {
|
||||||
max-width: 980px;
|
max-width: 980px;
|
||||||
@@ -1321,3 +1414,147 @@ img {
|
|||||||
.cgu-card br {
|
.cgu-card br {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==== ANIMATIONS & INTERACTIONS ==== */
|
||||||
|
|
||||||
|
/* Transition entre les pages : fondu de l'ancienne page vers la nouvelle
|
||||||
|
(View Transitions API, navigateurs récents uniquement, sans effet ailleurs) */
|
||||||
|
@view-transition {
|
||||||
|
navigation: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root) {
|
||||||
|
animation: pageOut 0.2s ease both;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: pageIn 0.3s ease both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pageOut {
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pageIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Entrée du contenu à chaque chargement de page (pour les navigateurs sans View Transitions) */
|
||||||
|
main {
|
||||||
|
animation: pageEnter 0.45s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pageEnter {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(12px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Si le navigateur gère les View Transitions on évite de cumuler les deux entrées */
|
||||||
|
@supports (view-transition-name: root) {
|
||||||
|
main {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apparition au défilement : la classe .reveal est posée en JS (footer),
|
||||||
|
puis .visible est ajoutée quand l'élément entre dans l'écran */
|
||||||
|
.reveal {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(22px);
|
||||||
|
transition: opacity 0.55s ease, transform 0.55s ease;
|
||||||
|
transition-delay: var(--reveal-delay, 0s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Micro-interactions ---- */
|
||||||
|
|
||||||
|
/* Liens de navigation : légère élévation au survol */
|
||||||
|
.site-nav a {
|
||||||
|
transition: color 0.2s ease, background-color 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav a:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icône du bouton de thème : rotation au survol */
|
||||||
|
.theme-toggle i {
|
||||||
|
transition: transform 0.35s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover i {
|
||||||
|
transform: rotate(40deg) scale(1.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tags : petite élévation au survol */
|
||||||
|
.tag {
|
||||||
|
transition: transform 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Items de compétences : glissement vers la droite au survol */
|
||||||
|
.skill-list li {
|
||||||
|
transition: border-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-list li:hover {
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Liens du footer : légère élévation au survol */
|
||||||
|
.footer-links a,
|
||||||
|
.footer-links .discord-handle {
|
||||||
|
display: inline-block;
|
||||||
|
transition: color 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links a:hover,
|
||||||
|
.footer-links .discord-handle:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Effet d'appui sur tous les boutons */
|
||||||
|
.btn:active,
|
||||||
|
.btn-submit:active,
|
||||||
|
.login-card input[type="submit"]:active,
|
||||||
|
.contact-form input[type="submit"]:active {
|
||||||
|
transform: translateY(1px) scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On désactive tout si l'utilisateur préfère un affichage sans mouvement */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
main,
|
||||||
|
::view-transition-old(root),
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav a:hover,
|
||||||
|
.tag:hover,
|
||||||
|
.skill-list li:hover,
|
||||||
|
.footer-links a:hover,
|
||||||
|
.footer-links .discord-handle:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user