This commit is contained in:
2025-12-24 14:48:49 +01:00
parent c6793e2473
commit 9beb6e1b43
20 changed files with 1777 additions and 508 deletions
-36
View File
@@ -1,36 +0,0 @@
<?php
header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php';
$input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['action']) || !isset($input['event_id'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Action ou ID manquant.']);
exit;
}
$action = $input['action'];
$eventId = $input['event_id'];
try {
if ($action === 'delete') {
$stmt = $pdo->prepare("DELETE FROM pf_events WHERE id = ?");
$stmt->execute([$eventId]);
echo json_encode(['status' => 'success', 'message' => 'Événement supprimé.']);
} elseif ($action === 'update' && isset($input['new_type'])) {
$stmt = $pdo->prepare("UPDATE pf_events SET event_type = ? WHERE id = ?");
$stmt->execute([$input['new_type'], $eventId]);
echo json_encode(['status' => 'success', 'message' => 'Événement mis à jour.']);
} else {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Action non valide ou données manquantes.']);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>
+43 -37
View File
@@ -3,17 +3,22 @@
ini_set('display_errors', 1); ini_set('display_errors', 1);
error_reporting(E_ALL); error_reporting(E_ALL);
// On se connecte à la base de données require __DIR__ . '/includes/auth.php';
require_login('/family-calendar.php');
require __DIR__ . '/includes/db.php'; require __DIR__ . '/includes/db.php';
// --- On récupère TOUS les événements sauvegardés en base --- // --- On récupère TOUS les événements sauvegardés en base ---
$stmt_events = $pdo->query("SELECT * FROM pf_events"); $stmt_events = $pdo->query("SELECT * FROM pf_events");
$dbEvents = $stmt_events->fetchAll(); $dbEvents = $stmt_events->fetchAll();
// --- Configuration de la page --- $pageTitle = "PachaFamily - Family Calendar";
$pageTitle = "PachaFamily - Family Calendar";
$activePage = "family-calendar"; $activePage = "family-calendar";
$bodyClass = "pf-family-calendar";
$pageCss = "/modules/family-calendar/family-calendar.css";
require __DIR__ . '/header.php'; require __DIR__ . '/header.php';
?> ?>
<!-- ===================================================================== --> <!-- ===================================================================== -->
@@ -26,7 +31,6 @@ require __DIR__ . '/header.php';
<h1>Family Calendar</h1> <h1>Family Calendar</h1>
<!-- ===================================================================== --> <!-- ===================================================================== -->
<!-- PANNEAU DE CONTRÔLE : Légende, Récapitulatif et Vacances --> <!-- PANNEAU DE CONTRÔLE : Légende, Récapitulatif et Vacances -->
<!-- ===================================================================== --> <!-- ===================================================================== -->
@@ -38,24 +42,33 @@ require __DIR__ . '/header.php';
<h2 class="pf-card-title">Légende</h2> <h2 class="pf-card-title">Légende</h2>
<div class="pf-card-body"> <div class="pf-card-body">
<div class="pf-legend-item"> <div class="pf-legend-item">
<div class="pf-legend-color fc-day--school-holiday"></div> <div class="pf-legend-color fc-legend-school-holiday"></div>
<span>Vacances scolaires</span> <span>Vacances scolaires</span>
</div> </div>
<div class="pf-legend-item"> <div class="pf-legend-item">
<div class="pf-legend-color fc-day--public-holiday"></div> <div class="pf-legend-color fc-legend-public-holiday"></div>
<span>Jour férié</span> <span>Jour férié</span>
</div> </div>
<div class="pf-legend-item"> <div class="pf-legend-item">
<div class="pf-legend-color fc-day--off-carole"></div> <div class="pf-legend-color fc-legend-off-carole"></div>
<span>Off Carole</span> <span>Off Carole</span>
</div> </div>
<div class="pf-legend-item"> <div class="pf-legend-item">
<div class="pf-legend-color fc-day--extra-off-carole"></div> <div class="pf-legend-color fc-legend-extra-off-carole"></div>
<span>Extra Off Carole</span> <span>Extra Off Carole</span>
</div> </div>
<div class="pf-legend-item">
<div class="pf-legend-color fc-legend-centre"></div>
<span>Centre</span>
</div>
<div class="pf-legend-item">
<div class="pf-legend-color fc-legend-avis"></div>
<span>Avis</span>
</div>
</div> </div>
</div> </div>
<!-- RÉCAPITULATIF ANNUEL --> <!-- RÉCAPITULATIF ANNUEL -->
<div class="pf-card pf-card--small"> <div class="pf-card pf-card--small">
<h2 class="pf-card-title">Récapitulatif annuel</h2> <h2 class="pf-card-title">Récapitulatif annuel</h2>
@@ -65,31 +78,30 @@ require __DIR__ . '/header.php';
</div> </div>
<!-- VACANCES SCOLAIRES --> <!-- VACANCES SCOLAIRES -->
<div class="pf-card pf-card--small pf-card--wide"> <div class="pf-card pf-card--small pf-card--wide">
<h2 class="pf-card-title">Vacances scolaires - Zone C (2025-2026)</h2> <h2 class="pf-card-title">Vacances scolaires - Zone C (2025-2026)</h2>
<div class="pf-card-body"> <div class="pf-card-body">
<div class="pf-table-wrapper"> <div class="pf-table-wrapper">
<table id="schoolHolidaysTable" class="fc-holidays-table"> <table id="schoolHolidaysTable" class="fc-holidays-table">
<thead> <thead>
<tr> <tr>
<th>Période</th> <th>Période</th>
<th>Du</th> <th>Du</th>
<th>Au</th> <th>Au</th>
<th>Zones</th> <th>Zones</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- Rempli par JS --> <!-- Rempli par JS -->
</tbody> </tbody>
</table> </table>
</div>
</div>
</div> </div>
</div> </div>
</div>
</section> </section>
<!-- ===================================================================== --> <!-- ===================================================================== -->
<!-- CALENDRIER MENSUEL --> <!-- CALENDRIER MENSUEL -->
<!-- ===================================================================== --> <!-- ===================================================================== -->
@@ -110,15 +122,11 @@ require __DIR__ . '/header.php';
</div> </div>
<div class="fc-calendar-and-summary"> <div class="fc-calendar-and-summary">
<div id="fc-month-calendar" class="fc-month-calendar"> <div id="fc-month-calendar" class="fc-month-calendar">
<!-- Le calendrier mensuel sera généré par JavaScript -->
</div>
<div id="fc-month-summary" class="fc-month-summary">
<!-- Le tableau récapitulatif sera généré par JavaScript -->
</div>
</div> </div>
<!-- Le menu contextuel pour le calendrier mensuel --> <!-- Le menu contextuel pour le calendrier mensuel -->
<div id="fc-month-selectionMenu" class="fc-selection-menu"></div> <div id="fc-month-selectionMenu" class="fc-selection-menu"></div>
</div> </div>
</div>
</section> </section>
<!-- ===================================================================== --> <!-- ===================================================================== -->
@@ -153,12 +161,10 @@ require __DIR__ . '/header.php';
</div> </div>
</section> </section>
<!-- ===================================================================== --> <!-- ===================================================================== -->
<!-- CHARGEMENT DU SCRIPT JAVASCRIPT PRINCIPAL --> <!-- CHARGEMENT DU SCRIPT JAVASCRIPT PRINCIPAL -->
<!-- ===================================================================== --> <!-- ===================================================================== -->
<script src="/assets/js/family-calendar.js"></script> <script src="/modules/family-calendar/family-calendar.js"></script>
<?php <?php
// Inclusion du pied de page // Inclusion du pied de page
+2
View File
@@ -5,5 +5,7 @@
<small>&copy; <?php echo date('Y'); ?> PachaFamily</small> <small>&copy; <?php echo date('Y'); ?> PachaFamily</small>
</div> </div>
</footer> </footer>
</div> <!-- FERMETURE pf-page -->
</body> </body>
</html> </html>
+209
View File
@@ -0,0 +1,209 @@
/* === RESET SIMPLE === */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* === BASE HTML / BODY === */
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
background: #f5f5f5;
color: #222;
}
/* === LAYOUT GLOBAL (structure page + footer collé en bas) === */
.pf-page {
min-height: 100vh; /* au minimum la hauteur de la fenêtre */
display: flex;
flex-direction: column;
}
.pf-main {
flex: 1 0 auto;
}
.pf-footer {
flex-shrink: 0;
}
/* === LAYOUT CONTAINER / HEADER / NAV / FOOTER === */
.pf-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
}
.pf-header {
background: #243b53;
color: #fff;
padding: 8px 0;
}
.pf-header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.pf-logo {
font-weight: 600;
font-size: 18px;
}
.pf-nav {
display: flex;
gap: 16px;
}
.pf-nav-link {
color: #d9e2ec;
text-decoration: none;
font-size: 14px;
padding: 4px 8px;
border-radius: 4px;
}
.pf-nav-link:hover {
background: rgba(255, 255, 255, 0.1);
}
.pf-nav-link--active {
background: #102a43;
color: #f0f4f8;
}
.pf-main {
padding: 24px 10px 48px;
}
.pf-section {
margin-top: 32px;
}
.pf-section--panel {
background: #fff;
border-radius: 8px;
padding: 16px 16px 20px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
.pf-footer {
border-top: 1px solid #d9e2ec;
padding: 8px 0;
font-size: 12px;
color: #627d98;
background: #f0f4f8;
}
/* Sur certaines pages (comme la home), on pourra override le header via une classe de body */
/* === FLEX & CARDS GÉNÉRIQUES === */
.pf-flex {
display: flex;
}
.pf-flex--wrap {
flex-wrap: wrap;
}
.pf-gap-lg {
gap: 24px;
}
.pf-card {
background: #f8fafc;
border-radius: 8px;
border: 1px solid #d9e2ec;
padding: 12px 14px;
}
.pf-card--small {
min-width: 240px;
max-width: 260px;
}
/* Large card (utilisée pour le tableau des vacances) */
.pf-card.pf-card--wide {
min-width: 400px;
flex: 1 1 400px;
}
.pf-card-title {
margin: 0 0 8px;
font-size: 15px;
}
.pf-card-body {
font-size: 13px;
}
/* === TABLES GÉNÉRIQUES === */
.pf-table-wrapper {
max-height: 600px;
overflow: auto;
border-radius: 12px;
background: #ffffff;
border: 1px solid #d9e2ec;
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08);
}
.pf-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.pf-table th,
.pf-table td {
border: 1px solid #d9e2ec;
padding: 4px 6px;
text-align: center;
position: relative;
}
.pf-table thead th {
position: sticky;
top: 0;
background: #f0f4f8;
z-index: 1;
}
.pf-table--compact th,
.pf-table--compact td {
padding: 3px 4px;
}
/* === FORMULAIRES / TEXTE GÉNÉRIQUE === */
label {
font-size: 12px;
}
input[type="number"] {
width: 3.1rem;
font-size: 11px;
padding: 2px 3px;
}
/* Récapitulatif global (utilisé dans le calendrier mais style simple) */
#globalSummary p {
margin: 4px 0;
font-size: 12px;
}
#globalSummary strong {
font-weight: 600;
color: #243b53;
}
+42 -17
View File
@@ -1,4 +1,9 @@
<?php <?php
// S'assurer que la session est démarrée si on veut afficher l'utilisateur
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
if (!isset($pageTitle)) { if (!isset($pageTitle)) {
$pageTitle = "PachaFamily"; $pageTitle = "PachaFamily";
} }
@@ -7,23 +12,43 @@ if (!isset($activePage)) {
} }
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr" class="<?= htmlspecialchars($bodyClass ?? '') ?>">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title><?php echo htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8'); ?></title> <title><?= htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8'); ?></title>
<link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
<header class="pf-header">
<div class="pf-container pf-header-content">
<div class="pf-logo">PachaFamily</div>
<nav class="pf-nav">
<a href="/index.php"
class="pf-nav-link <?php echo $activePage === 'home' ? 'pf-nav-link--active' : ''; ?>">Accueil</a>
<a href="/family-calendar.php"
class="pf-nav-link <?php echo $activePage === 'family-calendar' ? 'pf-nav-link--active' : ''; ?>">Family calendar</a>
</nav>
</div>
</header>
<main class="pf-container pf-main"> <!-- CSS globale -->
<link rel="stylesheet" href="/global.css">
<!-- CSS spécifique à la page (optionnel) -->
<?php if (!empty($pageCss)): ?>
<link rel="stylesheet" href="<?= htmlspecialchars($pageCss, ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
</head>
<body class="<?= htmlspecialchars($bodyClass ?? '') ?>">
<div class="pf-page"><!-- WRAPPER OUVERT ICI -->
<header class="pf-header">
<div class="pf-container pf-header-content">
<div class="pf-logo">PachaFamily</div>
<nav class="pf-nav">
<a href="/index.php"
class="pf-nav-link <?= $activePage === 'home' ? 'pf-nav-link--active' : ''; ?>">Accueil</a>
<a href="/family-calendar.php"
class="pf-nav-link <?= $activePage === 'family-calendar' ? 'pf-nav-link--active' : ''; ?>">Family calendar</a>
<?php if (isset($_SESSION['user'])): ?>
<span class="pf-nav-user">
Bonjour <?= htmlspecialchars($_SESSION['user']['display_name'] ?? $_SESSION['user']['username'], ENT_QUOTES, 'UTF-8'); ?>
</span>
<a href="/logout.php" class="pf-nav-link pf-nav-link--secondary">Se déconnecter</a>
<?php else: ?>
<a href="/login.php" class="pf-nav-link pf-nav-link--secondary">Se connecter</a>
<?php endif; ?>
</nav>
</div>
</header>
<main class="pf-container pf-main">
+19
View File
@@ -0,0 +1,19 @@
<?php
// includes/auth.php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
function require_login(string $redirectTarget = null): void
{
if (!isset($_SESSION['user'])) {
if ($redirectTarget === null) {
$redirectTarget = $_SERVER['REQUEST_URI'] ?? '/index.php';
}
$redirect = urlencode($redirectTarget);
header("Location: /login.php?redirect=$redirect");
exit;
}
}
-18
View File
@@ -1,18 +0,0 @@
<?php
$pageTitle = "PachaFamily - Accueil";
$activePage = "home";
require __DIR__ . '/header.php';
?>
<h1>Bienvenue sur PachaFamily</h1>
<p>Centre de controle de l'organisation familiale.</p>
<section class="pf-section">
<h2>Modules</h2>
<ul>
<li><a href="/family-calendar.php">Family calendar (conges, vacances, garde)</a></li>
</ul>
</section>
<?php
require __DIR__ . '/footer.php';
+68
View File
@@ -0,0 +1,68 @@
<?php
// Protection de la page : nécessite d'être connecté
require __DIR__ . '/includes/auth.php';
require_login('/index.php');
// Configuration de la page
$pageTitle = "PachaFamily - Accueil";
$activePage = "home";
$bodyClass = "pf-home";
$pageCss = "/modules/home/home.css";
require __DIR__ . '/header.php';
?>
<div class="pf-container">
<h1>Bienvenue sur PachaFamily</h1>
<p>Centre de contrôle de l'organisation familiale.</p>
<?php if (isset($_SESSION['user'])): ?>
<div class="pf-user-info">
Connecté en tant que
<strong><?= htmlspecialchars($_SESSION['user']['display_name'] ?? $_SESSION['user']['username']) ?></strong>
<a href="/logout.php">Se déconnecter</a>
</div>
<?php endif; ?>
<section class="pf-section">
<h2>Modules</h2>
<div class="pf-flex pf-flex--wrap pf-gap-lg pf-modules-cards">
<!-- Card : Family calendar -->
<a href="/family-calendar.php" class="pf-card pf-card--module">
<div class="pf-card-icon">📅</div>
<h3 class="pf-card-title">Family calendar</h3>
<div class="pf-card-body">
Gérez les congés, vacances et la garde sur un calendrier partagé
pour toute la famille.
</div>
<span class="pf-card-cta">Accéder au module</span>
</a>
<!-- Exemple de futur module -->
<div class="pf-card pf-card--module pf-card--disabled">
<div class="pf-card-icon">💰</div>
<h3 class="pf-card-title">Budget familial</h3>
<div class="pf-card-body">
Bientôt disponible : suivi des dépenses, catégories et budget mensuel.
</div>
<span class="pf-card-cta">À venir</span>
</div>
<!-- Autre exemple -->
<div class="pf-card pf-card--module pf-card--disabled">
<div class="pf-card-icon">📦</div>
<h3 class="pf-card-title">Liste de courses</h3>
<div class="pf-card-body">
Centralisez les listes de courses et les partages entre membres.
</div>
<span class="pf-card-cta">À venir</span>
</div>
</div>
</section>
</div>
<?php
require __DIR__ . '/footer.php';
+76
View File
@@ -0,0 +1,76 @@
<?php
session_start();
require __DIR__ . '/includes/db.php'; // Doit définir $pdo (PDO connecté à ta DB)
$pageTitle = "PachaFamily - Login";
$activePage = "login";
// Si l'utilisateur est déjà connecté, on le renvoie à l'accueil
if (isset($_SESSION['user'])) {
header('Location: /index.php');
exit;
}
$error = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if ($username === '' || $password === '') {
$error = "Merci de renseigner identifiant et mot de passe.";
} else {
// On récupère l'utilisateur dans la table pf_users
$stmt = $pdo->prepare("
SELECT id, username, password_hash, display_name
FROM pf_users
WHERE username = :username
");
$stmt->execute([':username' => $username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// Vérification du mot de passe
if ($user && password_verify($password, $user['password_hash'])) {
// Connexion OK : on stocke les infos utiles en session
$_SESSION['user'] = [
'id' => (int)$user['id'],
'username' => $user['username'],
'display_name' => $user['display_name'],
];
// Redirection : soit la page demandée, soit l'accueil
$redirectTo = $_GET['redirect'] ?? '/index.php';
header('Location: ' . $redirectTo);
exit;
} else {
$error = "Identifiants incorrects.";
}
}
}
require __DIR__ . '/header.php';
?>
<h1>Connexion</h1>
<?php if ($error): ?>
<div class="pf-error"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="post" action="/login.php<?= isset($_GET['redirect']) ? '?redirect=' . urlencode($_GET['redirect']) : '' ?>">
<div class="pf-form-group">
<label for="username">Identifiant</label>
<input type="text" id="username" name="username" required autofocus>
</div>
<div class="pf-form-group">
<label for="password">Mot de passe</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">Se connecter</button>
</form>
<?php
require __DIR__ . '/footer.php';
+26
View File
@@ -0,0 +1,26 @@
<?php
session_start();
// On vide toutes les variables de session
$_SESSION = [];
// On détruit la session
session_destroy();
// Optionnel : supprimer le cookie de session (plus propre)
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
// On renvoie vers la page de login (ou index si tu préfères)
header('Location: /login.php');
exit;
@@ -0,0 +1,31 @@
<?php
// api/get-leaves.php
header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php';
try {
$stmt = $pdo->query("
SELECT
l.id,
l.person_id,
p.name AS person_name,
l.leave_type,
l.leave_date,
l.duration
FROM pf_leaves l
JOIN pf_people p ON p.id = l.person_id
ORDER BY l.leave_date, l.person_id
");
$leaves = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
'status' => 'success',
'leaves' => $leaves,
]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
@@ -0,0 +1,103 @@
<?php
header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php';
$input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['action'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Action manquante.']);
exit;
}
$action = $input['action'];
try {
if ($action === 'delete') {
// Suppression d'un seul événement
if (!isset($input['event_id'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'ID manquant pour la suppression.']);
exit;
}
$eventId = (int)$input['event_id'];
$stmt = $pdo->prepare("DELETE FROM pf_events WHERE id = ?");
$stmt->execute([$eventId]);
echo json_encode(['status' => 'success', 'message' => 'Événement supprimé.']);
} elseif ($action === 'update') {
// Mise à jour d'un seul événement
if (!isset($input['event_id'], $input['new_type'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'ID ou nouveau type manquant pour la mise à jour.']);
exit;
}
$eventId = (int)$input['event_id'];
$newType = $input['new_type'];
$stmt = $pdo->prepare("UPDATE pf_events SET event_type = ? WHERE id = ?");
$stmt->execute([$newType, $eventId]);
echo json_encode(['status' => 'success', 'message' => 'Événement mis à jour.']);
} elseif ($action === 'bulk_delete') {
// Suppression en masse
if (empty($input['event_ids']) || !is_array($input['event_ids'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Liste event_ids manquante pour bulk_delete.']);
exit;
}
$eventIds = array_map('intval', $input['event_ids']);
$eventIds = array_filter($eventIds, fn($id) => $id > 0);
if (empty($eventIds)) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Aucun ID valide pour bulk_delete.']);
exit;
}
$placeholders = implode(',', array_fill(0, count($eventIds), '?'));
$stmt = $pdo->prepare("DELETE FROM pf_events WHERE id IN ($placeholders)");
$stmt->execute($eventIds);
echo json_encode(['status' => 'success', 'message' => 'Événements supprimés en masse.']);
} elseif ($action === 'bulk_update') {
// Mise à jour en masse
if (empty($input['event_ids']) || !is_array($input['event_ids']) || !isset($input['new_type'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'event_ids ou new_type manquant pour bulk_update.']);
exit;
}
$eventIds = array_map('intval', $input['event_ids']);
$eventIds = array_filter($eventIds, fn($id) => $id > 0);
$newType = $input['new_type'];
if (empty($eventIds)) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Aucun ID valide pour bulk_update.']);
exit;
}
$placeholders = implode(',', array_fill(0, count($eventIds), '?'));
$params = array_merge([$newType], $eventIds);
$stmt = $pdo->prepare("UPDATE pf_events SET event_type = ? WHERE id IN ($placeholders)");
$stmt->execute($params);
echo json_encode(['status' => 'success', 'message' => 'Événements mis à jour en masse.']);
} else {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Action non valide.']);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
@@ -0,0 +1,45 @@
<?php
// api/manage-leaf.php
header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php';
$input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['action'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Action manquante.']);
exit;
}
$action = $input['action'];
try {
if ($action === 'delete_day') {
if (empty($input['date'])) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Date manquante pour delete_day.']);
exit;
}
$dateObj = date_create($input['date']);
if (!$dateObj) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Date invalide.']);
exit;
}
$leaveDate = $dateObj->format('Y-m-d');
$stmt = $pdo->prepare("DELETE FROM pf_leaves WHERE leave_date = ?");
$stmt->execute([$leaveDate]);
echo json_encode(['status' => 'success', 'message' => 'Congés supprimés pour ce jour.']);
} else {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Action non valide.']);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
@@ -0,0 +1,68 @@
<?php
// api/save-leaves.php
header('Content-Type: application/json');
require __DIR__ . '/../includes/db.php';
$input = json_decode(file_get_contents('php://input'), true);
if (!is_array($input) || empty($input)) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Données invalides (attendu tableau JSON).']);
exit;
}
// Exemple d'élément attendu :
// { date: '2025-09-01', person_id: 2, leave_type: 'CP', duration: 1.0 }
$validLeaveTypes = ['CP', 'JRA', 'JA'];
try {
$pdo->beginTransaction();
$stmtInsert = $pdo->prepare("
INSERT INTO pf_leaves (person_id, leave_type, leave_date, duration)
VALUES (:person_id, :leave_type, :leave_date, :duration)
");
foreach ($input as $item) {
$date = $item['date'] ?? null;
$personId = $item['person_id'] ?? null;
$leaveType = $item['leave_type'] ?? null;
$duration = isset($item['duration']) ? (float)$item['duration'] : 1.0;
if (!$date || !$personId || !$leaveType) {
continue; // on ignore les lignes incomplètes
}
if (!in_array($leaveType, $validLeaveTypes, true)) {
continue; // leave_type invalide
}
// Normalisation de la date
$dateObj = date_create($date);
if (!$dateObj) {
continue;
}
$leaveDate = $dateObj->format('Y-m-d');
$stmtInsert->execute([
':person_id' => (int)$personId,
':leave_type' => $leaveType,
':leave_date' => $leaveDate,
':duration' => $duration,
]);
}
$pdo->commit();
echo json_encode([
'status' => 'success',
'message' => 'Congés enregistrés.',
]);
} catch (PDOException $e) {
$pdo->rollBack();
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
@@ -1,94 +1,6 @@
/* === RESET SIMPLE === */ /* === TABLES SPÉCIFIQUES AU FAMILY CALENDAR === */
*,
*::before,
*::after {
box-sizing: border-box;
}
body { /* Tableau des vacances scolaires */
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
background: #f5f5f5;
color: #222;
}
/* === LAYOUT GLOBAL === */
.pf-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
}
.pf-header {
background: #243b53;
color: #fff;
padding: 8px 0;
}
.pf-header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.pf-logo {
font-weight: 600;
font-size: 18px;
}
.pf-nav {
display: flex;
gap: 16px;
}
.pf-nav-link {
color: #d9e2ec;
text-decoration: none;
font-size: 14px;
padding: 4px 8px;
border-radius: 4px;
}
.pf-nav-link:hover {
background: rgba(255, 255, 255, 0.1);
}
.pf-nav-link--active {
background: #102a43;
color: #f0f4f8;
}
.pf-main {
padding: 24px 10px 48px;
}
.pf-section {
margin-top: 24px;
}
.pf-section--panel {
background: #fff;
border-radius: 8px;
padding: 16px 16px 20px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
.pf-footer {
border-top: 1px solid #d9e2ec;
padding: 8px 0;
font-size: 12px;
color: #627d98;
background: #f0f4f8;
}
/* === CARDS & FLEX === */
.pf-card.pf-card--wide {
min-width: 400px; /* tu peux monter à 500 ou 600 selon ton goût */
flex: 1 1 400px; /* laisse la carte prendre plus de place dans la ligne */
}
/* Le tableau des vacances occupe toute la largeur disponible */
#schoolHolidaysTable { #schoolHolidaysTable {
width: auto; width: auto;
table-layout: auto; table-layout: auto;
@@ -99,81 +11,39 @@ body {
width: 100%; width: 100%;
overflow-x: auto; overflow-x: auto;
} }
/* En-têtes et cellules sans retour à la ligne */ /* En-têtes et cellules sans retour à la ligne */
#schoolHolidaysTable th, #schoolHolidaysTable th,
#schoolHolidaysTable td { #schoolHolidaysTable td {
white-space: nowrap; /* pas de retour à la ligne dans les cellules */ white-space: nowrap;
overflow: visible; /* pas de tronquage */ overflow: visible;
padding: 4px 8px; padding: 4px 8px;
text-align: center; text-align: center;
} }
.pf-flex { /* Planning hebdomadaire principal */
display: flex;
}
.pf-flex--wrap {
flex-wrap: wrap;
}
.pf-gap-lg {
gap: 24px;
}
.pf-card {
background: #f8fafc;
border-radius: 8px;
border: 1px solid #d9e2ec;
padding: 12px 14px;
}
.pf-card--small {
min-width: 240px;
max-width: 260px;
}
.pf-card-title {
margin: 0 0 8px;
font-size: 15px;
}
.pf-card-body {
font-size: 13px;
}
label {
font-size: 12px;
}
/* === TABLES === */
/* Appliquer un layout fixe pour harmoniser la largeur des colonnes */
#planningTable { #planningTable {
table-layout: fixed; table-layout: fixed;
width: 100%; /* le tableau prend toute la largeur disponible */ width: 100%;
} }
/* Colonnes : texte centré et césures possibles */
#planningTable th, #planningTable th,
#planningTable td { #planningTable td {
text-align: center; text-align: center;
white-space: nowrap; /* évite les retours à la ligne sauvages */ white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; /* coupe avec "..." si le texte déborde */ text-overflow: ellipsis;
} }
/* Colonnes "Mois" et "Semaine" un peu plus étroites */ /* Colonnes "Mois" et "Semaine" plus étroites */
#planningTable th:nth-child(1), #planningTable th:nth-child(1),
#planningTable td:nth-child(1) { #planningTable td:nth-child(1),
width: 2%; /* Mois */
}
#planningTable th:nth-child(2), #planningTable th:nth-child(2),
#planningTable td:nth-child(2) { #planningTable td:nth-child(2) {
width: 2%; /* Semaine */ width: 2%;
} }
/* Colonnes jours (Lundi à Vendredi) réparties sur une largeur raisonnable */ /* Colonnes jours (Lundi à Vendredi) */
#planningTable th:nth-child(3), #planningTable th:nth-child(3),
#planningTable td:nth-child(3), #planningTable td:nth-child(3),
#planningTable th:nth-child(4), #planningTable th:nth-child(4),
@@ -184,10 +54,10 @@ label {
#planningTable td:nth-child(6), #planningTable td:nth-child(6),
#planningTable th:nth-child(7), #planningTable th:nth-child(7),
#planningTable td:nth-child(7) { #planningTable td:nth-child(7) {
width: 2%; /* ajustable selon ton rendu */ width: 2%;
} }
/* Colonnes de totaux (Off / Extra / Centre / Avis) un peu plus compactes */ /* Colonnes de totaux */
#planningTable th:nth-child(8), #planningTable th:nth-child(8),
#planningTable td:nth-child(8), #planningTable td:nth-child(8),
#planningTable th:nth-child(9), #planningTable th:nth-child(9),
@@ -199,48 +69,8 @@ label {
width: 4%; width: 4%;
} }
.pf-table-wrapper { /* === COLOR CODING / LÉGENDE === */
max-height: 600px;
overflow: auto;
border-radius: 8px;
background: #fff;
border: 1px solid #d9e2ec;
}
.pf-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.pf-table th,
.pf-table td {
border: 1px solid #d9e2ec;
padding: 4px 6px;
text-align: center;
position: relative;
}
.pf-table thead th {
position: sticky;
top: 0;
background: #f0f4f8;
z-index: 1;
}
.pf-table--compact th,
.pf-table--compact td {
padding: 3px 4px;
}
/* === INPUTS === */
input[type="number"] {
width: 3.1rem;
font-size: 11px;
padding: 2px 3px;
}
/* === COLOR CODING POUR LE FAMILY CALENDAR === */
.fc-row--carole-off { .fc-row--carole-off {
background: #ffe0e0; background: #ffe0e0;
} }
@@ -254,30 +84,41 @@ input[type="number"] {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
font-size: 12px; font-size: 12px;
margin-top: 12px; margin-top: 8px;
} }
.fc-holidays-table th, .fc-holidays-table th,
.fc-holidays-table td { .fc-holidays-table td {
border: 1px solid #d9e2ec; border: none;
padding: 4px 6px; padding: 4px 4px;
text-align: left;
} }
.fc-holidays-table th { .fc-holidays-table thead th {
background-color: #f0f4f8; background: none;
font-weight: 600;
color: #243b53;
padding-bottom: 4px;
border-bottom: 1px solid #d9e2ec;
}
.fc-holidays-table tbody tr:nth-child(odd) td {
background: #f8fafc;
}
.fc-holidays-table tbody tr:nth-child(even) td {
background: transparent;
} }
/* Cellule de jour sélectionnée dans le calendrier */ /* Cellule de jour sélectionnée dans le calendrier */
.fc-day--selected { .fc-day--selected {
background-color: #bde4ff !important; /* Une couleur de sélection claire, !important pour forcer le style */ background-color: #bde4ff !important;
cursor: pointer; cursor: pointer;
} }
/* Le menu contextuel flottant */ /* Le menu contextuel flottant */
.fc-selection-menu { .fc-selection-menu {
display: none; /* Caché par défaut */ display: none;
position: absolute; /* Permet de le positionner près de la souris */ position: absolute;
z-index: 100; z-index: 100;
background-color: white; background-color: white;
border: 1px solid #ccc; border: 1px solid #ccc;
@@ -290,6 +131,7 @@ input[type="number"] {
.fc-selection-menu .fc-menu-section { .fc-selection-menu .fc-menu-section {
margin-bottom: 8px; margin-bottom: 8px;
} }
.fc-selection-menu .fc-menu-section:last-child { .fc-selection-menu .fc-menu-section:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@@ -334,25 +176,12 @@ input[type="number"] {
box-sizing: border-box; box-sizing: border-box;
} }
/* Picto pour Centre (toboggan/balançoire) */ /* Picto pour Centre */
.fc-day--has-guard.fc-day--centre { .fc-day--has-guard.fc-day--centre {
position: relative; position: relative;
} }
.fc-day--has-guard.fc-day--centre::after { .fc-day--has-guard.fc-day--centre::after {
content: "🎢"; content: "🏫";
position: absolute;
top: 2px;
right: 2px;
font-size: 14px;
line-height: 1;
z-index: 2;
}
/* Picto pour Avis (FC Barcelone) */
.fc-day--has-guard.fc-day--avis {
position: relative;
}
.fc-day--has-guard.fc-day--avis::after {
content: "⚽";
position: absolute; position: absolute;
top: 2px; top: 2px;
right: 2px; right: 2px;
@@ -361,31 +190,131 @@ input[type="number"] {
z-index: 2; z-index: 2;
} }
/* --- Combinaisons avec dégradés --- */ /* Combinaisons avec dégradés */
/* Combinaison Vacances + Off Carole */
.fc-day--school-holiday.fc-day--off-carole { .fc-day--school-holiday.fc-day--off-carole {
background-image: linear-gradient(45deg, #e5d9f2 49%, #ffe9a7 51%); background-image: linear-gradient(45deg, #e5d9f2 49%, #ffe9a7 51%);
} }
/* Combinaison Vacances + Extra Off Carole */
.fc-day--school-holiday.fc-day--extra-off-carole { .fc-day--school-holiday.fc-day--extra-off-carole {
background-image: linear-gradient(45deg, #e5d9f2 49%, #ffd59b 51%); background-image: linear-gradient(45deg, #e5d9f2 49%, #ffd59b 51%);
} }
/* Eléments pour la légende */ /* Légende */
.pf-legend-item { .pf-legend-item {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 5px; margin-bottom: 5px;
} }
.pf-legend-color { .pf-legend-color {
width: 20px; width: 18px;
height: 20px; height: 18px;
border: 1px solid #ccc; border-radius: 4px;
margin-right: 10px; border: 1px solid #d9e2ec;
flex-shrink: 0; margin-right: 8px;
}
.pf-legend-item span {
font-size: 12px;
color: #334e68;
}
.fc-legend-school-holiday {
background-color: #e5d9f2;
}
.fc-legend-public-holiday {
background-color: #e0e0e0;
}
.fc-legend-off-carole {
background-color: #ffe9a7;
}
.fc-legend-extra-off-carole {
background-color: #ffd59b;
}
.fc-legend-centre {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
background: none;
border: none;
}
.fc-legend-centre::before {
content: "🏫";
}
/* Avis indicateur */
.fc-legend-avis {
border-radius: 2px;
width: 24px;
height: 16px;
background: linear-gradient(
to bottom,
#ffcc00 0%,
#ffcc00 11%,
#c8102e 11%,
#c8102e 22%,
#ffcc00 22%,
#ffcc00 33%,
#c8102e 33%,
#c8102e 44%,
#ffcc00 44%,
#ffcc00 55%,
#c8102e 55%,
#c8102e 66%,
#ffcc00 66%,
#ffcc00 77%,
#c8102e 77%,
#c8102e 88%,
#ffcc00 88%,
#ffcc00 100%
);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
} }
/* === CALENDRIER MENSUEL === */ /* === CALENDRIER MENSUEL === */
.fc-day--has-guard.fc-day--avis {
position: relative;
}
.fc-day--has-guard.fc-day--avis::after {
content: "";
position: absolute;
top: 2px;
right: 2px;
width: 14px;
height: 8px;
border-radius: 2px;
background: linear-gradient(
to bottom,
#ffcc00 0%,
#ffcc00 11%,
#c8102e 11%,
#c8102e 22%,
#ffcc00 22%,
#ffcc00 33%,
#c8102e 33%,
#c8102e 44%,
#ffcc00 44%,
#ffcc00 55%,
#c8102e 55%,
#c8102e 66%,
#ffcc00 66%,
#ffcc00 77%,
#c8102e 77%,
#c8102e 88%,
#ffcc00 88%,
#ffcc00 100%
);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
z-index: 2;
}
.fc-month-calendar-wrapper { .fc-month-calendar-wrapper {
background: #fff; background: #fff;
border-radius: 8px; border-radius: 8px;
@@ -395,10 +324,7 @@ input[type="number"] {
} }
.fc-calendar-and-summary { .fc-calendar-and-summary {
display: grid; display: block;
grid-template-columns: 1fr auto;
gap: 24px;
align-items: start;
} }
.fc-month-header { .fc-month-header {
@@ -506,6 +432,36 @@ input[type="number"] {
transition: background-color 0.2s; transition: background-color 0.2s;
} }
.fc-month-summary-inline {
margin-top: 8px;
background: #f8fafc;
border-radius: 6px;
border: 1px solid #d9e2ec;
font-size: 12px;
padding: 6px 8px;
}
.fc-month-summary-inline table {
width: 100%;
border-collapse: collapse;
}
.fc-month-summary-inline th,
.fc-month-summary-inline td {
padding: 4px 6px;
text-align: center;
}
.fc-month-summary-inline th {
font-weight: 600;
color: #243b53;
border-bottom: 1px solid #d9e2ec;
}
.fc-month-summary-inline td:first-child {
text-align: left;
}
.fc-month-day { .fc-month-day {
background: #fff; background: #fff;
} }
@@ -515,9 +471,9 @@ input[type="number"] {
} }
.fc-day--other-month { .fc-day--other-month {
background: #f8fafc; background: #f0f4f8;
color: #9fb3c8; color: #9fb3c8;
cursor: default; box-shadow: none;
} }
.fc-day--weekend { .fc-day--weekend {
@@ -555,8 +511,9 @@ input[type="number"] {
.fc-month-table .fc-day--has-guard.fc-day--centre { .fc-month-table .fc-day--has-guard.fc-day--centre {
position: relative; position: relative;
} }
.fc-month-table .fc-day--has-guard.fc-day--centre::after { .fc-month-table .fc-day--has-guard.fc-day--centre::after {
content: "🎢"; content: "🏫";
position: absolute; position: absolute;
top: 2px; top: 2px;
right: 2px; right: 2px;
@@ -564,72 +521,96 @@ input[type="number"] {
line-height: 1; line-height: 1;
z-index: 2; z-index: 2;
} }
.fc-month-table .fc-day--has-guard.fc-day--avis { .fc-month-table .fc-day--has-guard.fc-day--avis {
position: relative; position: relative;
} }
.fc-month-table .fc-day--has-guard.fc-day--avis::after { .fc-month-table .fc-day--has-guard.fc-day--avis::after {
content: ""; content: "";
position: absolute; position: absolute;
top: 2px; top: 2px;
right: 2px; right: 2px;
font-size: 12px; width: 12px;
line-height: 1; height: 7px;
border-radius: 2px;
background: linear-gradient(
to bottom,
#ffcc00 0%,
#ffcc00 11%,
#c8102e 11%,
#c8102e 22%,
#ffcc00 22%,
#ffcc00 33%,
#c8102e 33%,
#c8102e 44%,
#ffcc00 44%,
#ffcc00 55%,
#c8102e 55%,
#c8102e 66%,
#ffcc00 66%,
#ffcc00 77%,
#c8102e 77%,
#c8102e 88%,
#ffcc00 88%,
#ffcc00 100%
);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
z-index: 2; z-index: 2;
} }
/* Combinaisons avec dégradés pour le calendrier mensuel */
.fc-month-table .fc-day--school-holiday.fc-day--off-carole {
background-image: linear-gradient(45deg, #e5d9f2 49%, #ffe9a7 51%);
}
.fc-month-table .fc-day--school-holiday.fc-day--extra-off-carole {
background-image: linear-gradient(45deg, #e5d9f2 49%, #ffd59b 51%);
}
.fc-month-table .fc-day--selected { .fc-month-table .fc-day--selected {
background-color: #bde4ff !important; background-color: #bde4ff !important;
cursor: pointer; cursor: pointer;
} }
.fc-leaves-label-container {
position: absolute;
top: 1px;
left: 2px;
display: flex;
flex-direction: column;
gap: 0;
z-index: 2;
}
.fc-leaves-label {
font-size: 8px;
font-weight: 600;
color: #1f2933;
line-height: 1;
}
/* === TABLEAU RÉCAPITULATIF === */ /* === TABLEAU RÉCAPITULATIF === */
.fc-month-summary { .fc-month-summary {
min-width: 300px; min-width: 300px;
} }
.fc-summary-table-wrapper { .fc-summary-table-wrapper {
background: #f8fafc; background: #f8fafc;
border-radius: 6px; border-radius: 10px;
padding: 12px; padding: 10px 10px 12px;
border: 1px solid #d9e2ec; border: 1px solid #d9e2ec;
box-shadow: 0 4px 10px rgba(15, 23, 42, 0.06);
} }
.fc-summary-table { .fc-summary-table {
width: 100%; font-size: 12px;
border-collapse: collapse;
font-size: 13px;
} }
.fc-summary-table thead th { .fc-summary-table thead th {
background: #f0f4f8; background: transparent;
padding: 8px 6px; border-bottom: 1px solid #d9e2ec;
text-align: center;
font-weight: 600;
color: #243b53;
border: 1px solid #d9e2ec;
white-space: nowrap;
} }
.fc-summary-table tbody td { .fc-summary-table tbody td {
border: 1px solid #d9e2ec; border: none;
padding: 6px 8px; border-bottom: 1px solid #edf2f7;
text-align: center;
background: #fff;
} }
.fc-summary-table tbody td:first-child { .fc-summary-table tbody tr:last-child td {
text-align: left; border-bottom: none;
font-weight: 500;
color: #243b53;
} }
.fc-summary-total-row { .fc-summary-total-row {
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 535 KiB

+117
View File
@@ -0,0 +1,117 @@
/* === HOME : fond et overlay === */
/* Fond image + overlay uniquement pour la home */
body.pf-home {
position: relative;
background-image: url("/modules/home/assets/img/background.jpg");
background-size: cover;
background-position: top center;
background-repeat: no-repeat;
}
/* Header translucide par-dessus l'image sur la home */
.pf-home .pf-header {
background: rgba(36, 59, 83, 0.85);
backdrop-filter: blur(4px); /* optionnel, si supporté */
}
/* Voile sombre sur toute la page home */
body.pf-home::before {
content: "";
position: fixed;
inset: 0;
background: rgba(15, 23, 42, 0.35);
z-index: -1;
}
/* S'assurer que le contenu passe au-dessus du voile */
body.pf-home > * {
position: relative;
z-index: 0;
}
/* === HOME / MODULES CARDS === */
/* Container des cartes de modules */
.pf-modules-cards {
margin-top: 16px;
}
/* Card module : réutilise .pf-card mais plus "clicable" */
.pf-card--module {
min-width: 240px;
max-width: 320px;
display: flex;
flex-direction: column;
text-decoration: none;
color: inherit;
background: #ffffff;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease,
border-color 0.15s ease, background-color 0.15s ease;
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.06);
}
.pf-card--module:hover {
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(15, 23, 42, 0.12);
border-color: #bcccdc;
background-color: #f8fafc;
}
/* Désactivation / "à venir" */
.pf-card--disabled {
cursor: default;
opacity: 0.7;
}
.pf-card--disabled:hover {
transform: none;
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.06);
border-color: #d9e2ec;
background-color: #f8fafc;
}
/* Icône dans la card */
.pf-card-icon {
font-size: 24px;
margin-bottom: 8px;
}
/* CTA (texte en bas de card) */
.pf-card-cta {
margin-top: 12px;
font-size: 12px;
font-weight: 600;
color: #243b53;
display: inline-flex;
align-items: center;
gap: 4px;
}
.pf-card-cta::after {
content: "→";
font-size: 12px;
}
/* Assure que le CTA reste en bas quand le texte est plus long */
.pf-card--module .pf-card-body {
flex: 1;
}
/* Bloc user info */
.pf-user-info {
margin-top: 12px;
font-size: 13px;
color: #4b5563;
}
.pf-user-info a {
color: #c81e1e;
text-decoration: none;
font-weight: 500;
}
.pf-user-info a:hover {
text-decoration: underline;
}