Ajout de la prise en charge des photos de profil

This commit is contained in:
2026-04-30 13:28:57 +02:00
committed by Soriba SYLLA
parent 7ee14e7b3f
commit 0047b1f91c
276 changed files with 45898 additions and 1 deletions

0
SIRH/__init__.py Normal file
View File

16
SIRH/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for SIRH project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'SIRH.settings')
application = get_asgi_application()

View File

@@ -12,21 +12,37 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
import os
from pathlib import Path
<<<<<<< HEAD
from decouple import config
=======
>>>>>>> c28b14f (clean: remove pycache from tracking)
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
<<<<<<< HEAD
=======
>>>>>>> c28b14f (clean: remove pycache from tracking)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
<<<<<<< HEAD
SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[]).split(',')
=======
SECRET_KEY = 'django-insecure--wdb9t(77rvyac$_q!n5gw86&0r(0&&j171v9h!-_$jahsza*5'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ["https://support.cerfig.org", "support.cerfig.org"]
>>>>>>> c28b14f (clean: remove pycache from tracking)
# Application definition
@@ -79,6 +95,7 @@ WSGI_APPLICATION = 'SIRH.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
<<<<<<< HEAD
if config('ENVIRONMENT') == 'local':
DATABASES = {
@@ -98,6 +115,26 @@ else:
'PORT': config('DATABASE_PORT'),
}
}
=======
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.mysql',
# 'NAME': 'sirh',
# 'USER': 'sirh',
# 'PASSWORD': 'sirh-cerfig',
# 'HOST': 'localhost',
# 'PORT': '3306',
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
>>>>>>> c28b14f (clean: remove pycache from tracking)
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators

BIN
SIRH/static/SIRH/PS1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

2078
SIRH/static/SIRH/icons.css Normal file

File diff suppressed because it is too large Load Diff

BIN
SIRH/static/SIRH/per1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
SIRH/static/SIRH/user.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -0,0 +1,157 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PRINCICALA</title>
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'styles.css' %}">
<link rel="stylesheet" href="{% static 'icons.css' %}">
</head>
<body>
<style>
.bg-orange-dark {
background: linear-gradient(90deg, #993d00, #b34700); /* oranges plus sombres */
color: white;
}
.sidebar .nav-link {
color: white;
font-weight: 500;
}
.sidebar .nav-link:hover {
background-color: rgba(255,255,255,0.2);
border-radius: 5px;
}
.logo {
max-width: 90px;
}
main {
margin-left: 220px;
}
.stat-card {
background: rgb(255, 255, 255);
padding: 20px;
border-radius: 8px;
box-shadow: 0px 2px 5px rgba(113, 11, 11, 0.1);
}
</style>
<div class="container-fluid">
<div class="row flex-nowrap">
<nav class="col-12 col-md-3 col-lg-2 sidebar p-3">
<div class="text-center mb-4">
<img src="{% static 'CERFIF.jpg' %}" alt="Logo" class="logo mb-5">
<h4 class="text-white">CERFIG</h4>
</div>
<ul class="nav flex-column">
<li class="nav-item mb-2">
<a href="/" class="nav-link"><i class="bi bi-speedometer2"></i> Tableau de bord</a>
</li>
<li class="nav-item mb-2">
<a href="#" class="nav-link"><i class=" bi bi-airplane"></i> Demande de congé </a>
</li>
<li class="nav-item mb-2">
<a href="#" class="nav-link"><i class="bi bi-people"></i> Gestion des Employés</a>
</li>
<li class="nav-item mb-2">
<a href="#" class="nav-link"><i class="bi bi-folder"></i> Gestion des Projets</a>
</li>
<li class="nav-item mb-2">
<a href="#" class="nav-link"><i class="bi bi-airplane"></i> Gestion des Congés</a>
</li>
<li class="nav-item mb-2">
<a href="#" class="nav-link"><i class="bi bi-graph-up"></i>Rapports et Statistiques</a>
</li>
<li class="nav-item mb-2">
<a href="#" class="nav-link"><i class="bi bi-gear"></i> Paramètres</a>
</li>
<li>
<a href="#" class="nav-link"><i class="bi bi-box-arrow-right"></i> Déconnexion</a>
</li>
</ul>
</nav>
<!-- Contenu principal -->
<main class="col px-4 py-4">
<h1 class="mb-4">Bienvenue sur le tableau de bord</h1>
<div class="row g-3 mb-4">
<div class="col-md-3">
<div class="stat-card">
<h5><i class="bi bi-airplane"></i> Congés en attente</h5>
<p class="display-6 fw-bold"> {{ nombre_conges_attente }}</p>
</div>
</div>
<div class="col-md-3">
<div class="stat-card">
<h5><i class="bi bi-people"></i> Nombre Employés </h5>
<p class="display-6 fw-bold">{{ nombre_employes }}</p>
</div>
</div>
<div class="col-md-3">
<div class="stat-card">
<h5><i class="bi bi-kanban"></i> Projets en cours</h5>
</div>
</div>
</div>
<div class="card p-3">
<h3>
<i class="bi bi-people"></i> La liste des employés en congé</h3>
<table class="table table-striped">
<thead>
<tr>
<th>Employé</th>
<th>Type de congé</th>
<th>Date de début</th>
<th>Date de fin</th>
<th>Statut</th>
</tr>
</thead>
<tbody>
{% for employe in personnes_en_conge %}
<tr>
<td>{{ employe.nom }} {{ employe.prenom }}</td>
<td>{{ employe.type }}</td>
<td>{{ employe.date_debut }}</td>
<td>{{ employe.date_fin }}</td>
<td>
<span class="badge bg-success">{{ employe.statut }} </span>
<td>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center">Personne en conge </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</main>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,197 @@
{% load static %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Connexion SI-RH</title>
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'styles.css' %}">
</head>
<body class="d-flex justify-content-center align-items-center min-vh-100 ">
<style>
.bg-orange-dark {
background: linear-gradient(90deg, #b35400, #cc6600);
color: white;
}
</style>
<!-- Formulaire de connexion -->
<div class="p-4 shadow-sm rounded bg-white" style="max-width: 400px; margin: auto;">
<img src="{% static 'CERFIF.jpg' %}" alt="Logo" class="logo mb-3"
style="display: block; margin-left: auto; margin-right: auto; width: 60%; max-width: 500px; height: auto;">
{% if messages %}
<!-- Modal message -->
<div class="modal fade" id="messageModal" tabindex="-1" aria-labelledby="messageModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-orange-dark
{% if messages.0.tags == 'error' %}bg-orange-dark text-white
{% elif messages.0.tags == 'success' %}bg-orange-dark text-white
{% else %}bg-orange-dark text-white{% endif %}">
<h5 class="modal-title" id="messageModalLabel">
{% if messages.0.tags == 'error' %}Erreur
{% elif messages.0.tags == 'success' %}Succès
{% else %}Information{% endif %}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
var messageModal = new bootstrap.Modal(document.getElementById('messageModal'));
messageModal.show();
});
</script>
{% endif %}
<form method="POST" action="{% url 'login' %}">
{% csrf_token %}
<div class="mb-3">
<label for="username">Nom dutilisateur</label>
<input type="text" name="username" class="form-control" placeholder="Entrez votre nom dutilisateur" required>
</div>
<div class="mb-3 position-relative">
<label for="password">Mot de passe</label>
<input type="password" name="password" class="form-control" placeholder="Entrez votre mot de passe" required>
<i class="bi bi-eye toggle-password" onclick="togglePassword()"
style="position:absolute; right:10px; top:38px; cursor:pointer;"></i>
</div>
<button type="submit" class="btn btn-primary w-100">Se connecter</button>
</form>
<p class="mt-3 text-center">
<a href="#" data-bs-toggle="modal" data-bs-target="#resetPasswordModal">Mot de passe oublié ?</a>
</p>
</div>
<!-- Modale de réinitialisation -->
<div class="modal fade" id="resetPasswordModal" tabindex="-1" aria-labelledby="resetPasswordModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content p-4">
<h5 class="modal-title mb-3" id="resetPasswordModalLabel">Réinitialisation du mot de passe</h5>
<form id="resetPasswordForm">
{% csrf_token %}
<div class="mb-3">
<label for="email">Entrez votre email</label>
<input type="email" name="email" class="form-control" placeholder="Email" required>
</div>
<div id="resetMessage" class="mb-3"></div>
<button type="submit" class="btn btn-warning w-100">Envoyer le lien</button>
</form>
</div>
</div>
</div>
{% if show_politique_modal %}
<!-- Modal Politique d'utilisation -->
<div class="modal fade" id="politiqueModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-orange-dark text-white">
<h5 class="modal-title">Politique d'utilisation de SI-RH</h5>
</div>
<div class="modal-body">
<p>Veuillez lire et accepter notre politique d'utilisation avant d'accéder à votre compte.</p>
<h6>Confidentialité des données</h6>
<p>Vos informations personnelles et professionnelles sont protégées. Toute divulgation non autorisée est strictement interdite.</p>
<h6>Utilisation autorisée</h6>
<p>Lapplication est réservée à un usage professionnel. Toute utilisation à des fins personnelles ou non autorisées est prohibée.</p>
<h6>Sécurité des comptes</h6>
<p>Ne partagez jamais vos identifiants. Changez votre mot de passe régulièrement et en cas de suspicion dintrusion.</p>
<h6>Responsabilités de lutilisateur</h6>
<p>Vous êtes responsable des actions effectuées via votre compte. Signalez toute anomalie ou problème à léquipe RH ou à ladministrateur.</p>
<h6>Acceptation et conformité</h6>
<p>En cliquant sur <strong>“Jaccepte”</strong>, vous confirmez avoir lu et accepté cette politique. Le non-respect peut entraîner une suspension ou une révocation de laccès.</p>
</div>
<div class="modal-footer">
<form method="POST" action="{% url 'accepter-politique' %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn btn-success">J'accepte</button>
</form>
<form method="POST" action="{% url 'refuser-politique' %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Refuser</button>
</form>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
var politiqueModal = new bootstrap.Modal(document.getElementById('politiqueModal'));
politiqueModal.show();
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
var politiqueModal = new bootstrap.Modal(document.getElementById('politiqueModal'));
politiqueModal.show();
});
</script>
>
<script>
document.addEventListener("DOMContentLoaded", function() {
var modal = new bootstrap.Modal(document.getElementById('politiqueModal'));
modal.show();
});
</script>
{% endif %}
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>
// Afficher / masquer le mot de passe
function togglePassword() {
const input = document.querySelector('input[name="password"]');
input.type = input.type === 'password' ? 'text' : 'password';
}
// AJAX pour réinitialisation
$('#resetPasswordForm').on('submit', function(e) {
e.preventDefault();
const email = $('input[name="email"]').val();
const csrfToken = $('[name=csrfmiddlewaretoken]').val();
$.ajax({
url: "{% url 'password_reset' %}",
type: 'POST',
data: {
'email': email,
'csrfmiddlewaretoken': csrfToken
},
success: function(response) {
$('#resetMessage').html('<div class="alert alert-success">Email envoyé ! Vérifiez votre boîte de réception.</div>');
},
error: function() {
$('#resetMessage').html('<div class="alert alert-danger">Erreur lors de lenvoi. Vérifiez lemail.</div>');
}
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,150 @@
{% load static %}
{% load roles %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PRINCICALA</title>
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'styles.css' %}">
</head>
<body>
<!-- ===================== -->
<!-- SIDEBAR RESPONSIVE -->
<!-- ===================== -->
<nav class="sidebar bg-orange-dark text-white d-none d-md-block col-md-3 col-lg-2 p-3"
style="background: linear-gradient(90deg, #993d00, #b34700); position: fixed; height: 100vh; overflow-y: auto;">
<!-- Profil utilisateur -->
<div class="text-center mb-4 d-flex flex-column align-items-center" style="margin-top:20px;">
<h6 class="fw-bold text-white mb-2">{{ user.first_name }} {{ user.last_name }}</h6>
<a href="{% url 'mon_profil' %}">
{% if user.photo %}
<img src="{{ user.photo.url }}" alt="Profil"
class="rounded-circle img-profil mx-auto"
style="width: 80px; height: 80px; object-fit: cover;">
{% else %}
<div class="rounded-circle bg-secondary d-flex justify-content-center align-items-center"
style="width: 80px; height: 80px;">
<i class="bi bi-person-circle text-white" style="font-size: 2rem;"></i>
</div>
{% endif %}
</a>
</div>
<!-- Liens du menu -->
<ul class="nav flex-column">
{% if user|has_role:"Employe" or user|has_role:"Administrateur" %}
<li class="nav-item mb-2">
<a href="{% url 'mon_profil' %}" class="nav-link text-white"><i class="bi bi-person-circle"></i> Mon profil</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'activites-projet' %}" class="nav-link text-white"><i class="bi bi-list-task"></i> Suivi des Activités</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'employe-conge' %}" class="nav-link text-white"><i class="bi bi-airplane"></i> Mes Congés</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'mes_formations' %}" class="nav-link text-white"><i class="bi bi-journal-text"></i> Mes certificats</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'salle' %}" class="nav-link text-white"><i class="bi bi-calendar-check"></i> Réserver une salle</a>
</li>
{% endif %}
{% if user|has_role:"RH" or user|has_role:"Directeur" or user|has_role:"Assistante" %}
<li class="nav-item mb-2">
<a href="{% url 'employe-index' %}" class="nav-link text-white"><i class="bi bi-speedometer2"></i> Tableau de bord</a>
</li>
{% if user|has_role:"Assistante" or user|has_role:"RH" %}
<li class="nav-item mb-2">
<a href="{% url 'activites-projet' %}" class="nav-link text-white"><i class="bi bi-list-task"></i> Suivi des Activités</a>
</li>
{% endif %}
{% if user|has_role:"Directeur" %}
<li class="nav-item mb-2">
<a href="{% url 'directeur' %}" class="nav-link text-white"><i class="bi bi-graph-up"></i> Suivi des Projets</a>
</li>
{% endif %}
<li class="nav-item mb-2">
<a href="{% url 'projet-index' %}" class="nav-link text-white"><i class="bi bi-folder"></i> Gestion des Projets</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'conge' %}" class="nav-link text-white"><i class="bi bi-airplane"></i> Congés</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'mes_formations' %}" class="nav-link text-white"><i class="bi bi-journal-text"></i> Mes certificats</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'salle' %}" class="nav-link text-white"><i class="bi bi-calendar-check"></i> Réserver une salle</a>
</li>
{% if user|has_role:"RH" or user|has_role:"Directeur" %}
<li class="nav-item mb-2">
<a href="{% url 'rapport-rh' %}" class="nav-link text-white"><i class="bi bi-graph-up"></i> Rapports et Statistiques</a>
</li>
{% endif %}
<li class="nav-item mb-2">
<a href="{% url 'mon_profil' %}" class="nav-link text-white"><i class="bi bi-person-circle"></i> Mon profil</a>
</li>
{% endif %}
{% if user|has_role:"Administrateur" %}
<li class="nav-item mb-2">
<a href="{% url 'gestion-utilisateurs' %}" class="nav-link text-white"><i class="bi bi-person-gear"></i> Gestion des Utilisateurs</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'parametres-rh' %}" class="nav-link text-white"><i class="bi bi-gear"></i> Paramètres</a>
</li>
{% endif %}
<li class="nav-item mb-2">
<a href="{% url 'deconnexion' %}" class="nav-link text-white"><i class="bi bi-box-arrow-right"></i> Déconnexion</a>
</li>
</ul>
</nav>
<!-- ===================== -->
<!-- MENU MOBILE (offcanvas) -->
<!-- ===================== -->
<!-- Bouton burger (visible uniquement sur téléphone) -->
<nav class="navbar navbar-dark bg-orange-dark d-md-none p-2" style="background: linear-gradient(90deg, #993d00, #b34700);">
<button class="btn btn-light" type="button" data-bs-toggle="offcanvas" data-bs-target="#menuMobile">
<i class="bi bi-list"></i> Menu
</button>
</nav>
<!-- Menu Offcanvas mobile -->
<div class="offcanvas offcanvas-start" tabindex="-1" id="menuMobile" style="background: linear-gradient(90deg, #993d00, #b34700); color: white;">
<div class="offcanvas-header">
<h5 class="offcanvas-title">{{ user.first_name }} {{ user.last_name }}</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<!-- Tu peux copier ici le même <ul> du menu principal -->
<ul class="nav flex-column">
<li class="nav-item mb-2">
<a href="{% url 'mon_profil' %}" class="nav-link text-white"><i class="bi bi-person-circle"></i> Mon profil</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'activites-projet' %}" class="nav-link text-white"><i class="bi bi-list-task"></i> Activités</a>
</li>
<li class="nav-item mb-2">
<a href="{% url 'deconnexion' %}" class="nav-link text-white"><i class="bi bi-box-arrow-right"></i> Déconnexion</a>
</li>
</ul>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,10 @@
<div class="alert alert-{{ message.tags }} d-flex align-items-center" role="alert">
{% if message.tags == 'success' %}
<i class="bi bi-check-circle-fill me-2"></i>
{% elif message.tags == 'warning' %}
<i class="bi bi-exclamation-triangle-fill me-2"></i>
{% elif message.tags == 'error' %}
<i class="bi bi-x-circle-fill me-2"></i>
{% endif %}
{{ message }}
</div>

View File

@@ -0,0 +1,384 @@
{% load static %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Profil utilisateur</title>
<!-- CSS -->
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'styles.css' %}">
<style>
.bg-orange-dark {
background: linear-gradient(90deg, #993d00, #b34700); /* oranges plus sombres */
color: white;
}
.card-body p { margin-bottom: 0.5rem; }
.main {
margin-left: 0px;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row flex-nowrap">
<!-- Sidebar -->
{% include 'menu_principal.html' %}
<!-- Main -->
<main class="col px-2 py-4" style="margin-left: 280px;">
{% if messages %}
<!-- Modal Message Django -->
<div class="modal fade" id="messageModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-orange-dark text-white">
<h5 class="modal-title">
{% if messages.0.tags == 'error' %}
Erreur
{% elif messages.0.tags == 'success' %}
Succès
{% else %}
Information
{% endif %}
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
<!-- Icône Notification -->
<div class="notification-bell position-fixed top-0 end-0 m-3" data-bs-toggle="modal" data-bs-target="#modalNotifications" style="z-index:1050; cursor:pointer;">
<i class="bi bi-bell-fill fs-4"></i>
<span class="badge bg-danger">{{ notifications_contrats|length }}</span>
</div>
<!-- Modal Notifications Contrats -->
<div class="modal fade" id="modalNotifications" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-orange-dark text-white">
<h5 class="modal-title"><i class="bi bi-bell me-2"></i>Contrats proches de la fin</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
{% if notifications_contrats %}
<ul class="list-group">
{% for notif in notifications_contrats %}
<h5>Votre Contrat <strong>{{ notif.numero }}</strong> signé le <strong>{{ notif.date }}</strong>
se termine dans
<span class="badge bg-danger rounded-pill">{{ notif.jours_restants }} jours</span>
</h5>
{% endfor %}
</ul>
{% else %}
<p class="text-muted">Aucun contrat proche de la fin.</p>
{% endif %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
<!-- Profil -->
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<div class="card border-0 shadow-sm">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<h4 class="mb-0 fw-bold">
<i class="bi bi-person-circle me-2"></i> Mon Profil
</h4>
</div>
<div class="card-body p-3">
<!-- Infos personnelles -->
<div class="row mb-3">
<div class="col-md-6">
<p><strong>Nom :</strong> {{ employe.last_name|default:"Non renseigné" }}</p>
<p><strong>Prénom :</strong> {{ employe.first_name|default:"Non renseigné" }}</p>
<p><strong>Matricule :</strong> {{ employe.matricule|default:"Non renseigné" }}</p>
</div>
<div class="col-md-6">
<p><strong>Département :</strong> {{ employe.departement|default:"Non renseigné" }}</p>
<p><strong>Email :</strong> {{ employe.email|default:"Non renseigné" }}</p>
<p><strong>Téléphone :</strong> {{ employe.telephone|default:"Non renseigné" }}</p>
</div>
</div>
<!-- Contrats -->
<h5 class="mt-3">Contrats</h5>
{% for contrat in contrats %}
<div class="mb-3 p-3 border rounded">
<div class="row">
<div class="col-md-6">
<p><strong>Numéro :</strong> {{ contrat.numero_contrat }}</p>
<p><strong>Type :</strong> {{ contrat.type_contrat }}</p>
<p><strong>Date début :</strong> {{ contrat.date_debut|date:"d/m/Y" }}</p>
<p><strong>Date fin :</strong> {{ contrat.date_fin|date:"d/m/Y"|default:"Non précisée" }}</p>
</div>
<div class="col-md-6">
<p><strong>Salaire mensuel :</strong>
{% if contrat.salaire_mensuel %}
{{ contrat.salaire_mensuel }} GNF
{% else %}
Non précisé
{% endif %}
</p>
<p><strong>Statut :</strong>
<span class="badge
{% if contrat.statut_auto == 'Actif' %}bg-success
{% elif contrat.statut_auto == 'Terminé' %}bg-danger
{% elif contrat.statut_auto == 'Suspendu' %}bg-warning text-dark
{% else %}bg-secondary{% endif %}">
{{ contrat.statut_auto }}
</span>
</p>
<p><strong>Solde congé :</strong> {{ contrat.solde_conge|default:"Non précisé" }} jours</p>
<p><strong>Fichier :</strong>
{% if contrat.fichier_contrat %}
<a href="{{ contrat.fichier_contrat.url }}" target="_blank">
<i class="bi bi-file-earmark-pdf me-1"></i> Télécharger
</a>
{% else %}
<span class="text-muted">Aucun fichier</span>
{% endif %}
</p>
</div>
</div>
</div>
{% empty %}
<p class="text-danger">Aucun contrat trouvé.</p>
{% endfor %}
<!-- Projets -->
<h5 class="mt-3">Projets</h5>
<ul class="list-group mb-3">
{% for a in affectations %}
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
{{ a.projet.nom_projet }} - {{ a.temps_affectation }}%
{% if a.projet.statut %}
- <span class="badge bg-info text-dark">{{ a.projet.statut }}</span>
{% endif %}
</div>
<div>
<button type="button" class="btn bg-orange-dark btn-sm text-white"
data-bs-toggle="modal" data-bs-target="#modalFicheProjet{{ a.projet.id }}">
<i class="bi bi-info-circle"></i> Détails
</button>
</div>
</li>
{% empty %}
<li class="list-group-item text-muted text-center">Aucun projet assigné.</li>
{% endfor %}
</ul>
</div>
<div class="card-footer bg-white d-flex justify-content-end">
<button type="button" class="btn btn-outline-secondary me-2" onclick="window.history.back()">Fermer</button>
<a href="{% url 'profil' %}" class="btn bg-orange-dark text-white">
<i class="bi bi-pencil-square me-1"></i> Renseigner les autres informations
</a>
</div>
</div>
</div>
</div>
</div>
{% for a in affectations %}
<!-- Modal Détails Projet -->
<div class="modal fade" id="modalFicheProjet{{ a.projet.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-orange-dark text-white">
<h5 class="modal-title">
<i class="bi bi-info-circle me-2"></i>Détails du projet {{ a.projet.nom_projet }}
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p><strong>Nom :</strong> {{ a.projet.nom_projet }}</p>
<p><strong>Numéro convention :</strong> {{ a.projet.numero_convention }}</p>
<p><strong>Dates :</strong> {{ a.projet.date_debut|date:"d/m/Y" }} → {{ a.projet.date_fin|date:"d/m/Y" }}</p>
<p><strong>Type :</strong> {{ a.projet.get_type_projet_display }}</p>
<p><strong>Domaine :</strong> {{ a.projet.get_domaine_recherche_display }}</p>
<p><strong>Budget :</strong> {{ a.projet.budget }} GNF</p>
<p><strong>Budget RH :</strong> {{ a.projet.budget_RH }} GNF</p>
<p><strong>Statut :</strong> <span class="badge bg-info">{{ a.projet.statut }}</span></p>
<p><strong>Description :</strong> {{ a.projet.description }}</p>
<h6 class="mt-4">Employés affectés :</h6>
<ul class="list-group mb-3">
{% for aff in a.projet.affectations.all %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ aff.employe }} <span class="badge bg-orange-dark">{{ aff.temps_affectation }}%</span>
</li>
{% empty %}
<li class="list-group-item text-muted">Aucun employé affecté</li>
{% endfor %}
</ul>
<h6>Bailleurs :</h6>
<ul class="list-group mb-3">
{% for f in a.projet.financements.all %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ f.bailleur.nom }} <span class="badge bg-success">{{ f.pourcentage }}%</span>
</li>
{% empty %}
<li class="list-group-item text-muted">Aucun bailleur enregistré</li>
{% endfor %}
</ul>
<h6 class="mt-4">Documents du projet :</h6>
<ul class="list-group">
{% for doc in a.projet.documents.all %}
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<strong>{{ doc.get_nom_document_display }}</strong><br>
pour le projet {{ a.projet.nom_projet }}<br>
{% if doc.description %}
<small class="text-muted">{{ doc.description }}</small><br>
{% endif %}
<small class="text-muted">
{% if doc.numero %}
{{ doc.numero }}
{% endif %}
</small>
<br> <small></small>
document ajouter le {{ doc.date_ajout|date:"d/m/Y" }}</small>
{% if doc.date_validite %}
<br><small class="text-muted"> et Valide jusqu'au {{ doc.date_validite|date:"d/m/Y" }}</small>
{% endif %}
</div>
</li>
{% empty %}
<li class="list-group-item text-muted">Aucun document disponible</li>
{% endfor %}
</ul>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle me-1"></i> Fermer
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</main>
</div>
</div>
<!-- JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Ferme les modales ouvertes avant d'en ouvrir une autre
const modals = document.querySelectorAll('.modal');
modals.forEach(modal => {
modal.addEventListener('show.bs.modal', function () {
document.querySelectorAll('.modal.show').forEach(openModal => {
if (openModal !== modal) {
const instance = bootstrap.Modal.getInstance(openModal);
if (instance) instance.hide();
}
});
});
});
// Répare le bug d'écran figé après fermeture
document.addEventListener('hidden.bs.modal', function () {
if (!document.querySelector('.modal.show')) {
document.body.classList.remove('modal-open');
document.body.style.removeProperty('padding-right');
}
});
const forms = document.querySelectorAll('form[action*="ajouter-document"]');
forms.forEach(form => {
form.addEventListener('submit', function (e) {
const btn = form.querySelector('button[type="submit"]');
const modalEl = form.closest('.modal');
if (btn) {
btn.disabled = true;
btn.innerHTML = '<i class="bi bi-hourglass-split"></i> Enregistrement...';
}
if (modalEl) {
const modalInstance = bootstrap.Modal.getInstance(modalEl);
if (modalInstance) modalInstance.hide();
}
});
});
const messageModalEl = document.getElementById('messageModal');
if (messageModalEl) {
const messageModal = new bootstrap.Modal(messageModalEl);
messageModal.show();
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,223 @@
{% load static %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Paramètres RH</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'styles.css' %}">
</head>
<body>
<style>
.bg-orange-dark {
background: linear-gradient(90deg, #993d00, #b34700);
color: white;
}
</style>
<div class="container-fluid">
<div class="row flex-nowrap">
<!-- Sidebar -->
{% include 'menu_principal.html' %}
<!-- Main -->
<main class="col px-2 py-4" style="margin-left: 280px;">
{% if messages %}
<!-- Modal message -->
<div class="modal fade" id="messageModal" tabindex="-1" aria-labelledby="messageModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-orange-dark
{% if messages.0.tags == 'error' %}bg-orange-dark text-white
{% elif messages.0.tags == 'success' %}bg-orange-dark text-white
{% else %}bg-orange-dark text-white{% endif %}">
<h5 class="modal-title" id="messageModalLabel">
{% if messages.0.tags == 'error' %}Erreur
{% elif messages.0.tags == 'success' %}Succès
{% else %}Information{% endif %}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
var messageModal = new bootstrap.Modal(document.getElementById('messageModal'));
messageModal.show();
});
</script>
{% endif %}
<h2 class="mb-4"><i class="bi bi-gear-fill"></i> Paramètres Administration</h2>
<div class="row">
<!-- Colonne Départements -->
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-2">
<h5><i class="bi bi-building"></i> Départements</h5>
<button class="btn bg-orange-dark btn-sm" data-bs-toggle="modal" data-bs-target="#modalAjouterDepartement">
<i class="bi bi-plus-circle"></i> Ajouter
</button>
</div>
<!-- Modal Ajout -->
<div class="modal fade" id="modalAjouterDepartement" tabindex="-1">
<div class="modal-dialog">
<form method="POST">
{% csrf_token %}
<input type="hidden" name="ajouter_departement" value="1">
<div class="modal-content">
<div class="modal-header bg-orange-dark text-white">
<h5 class="modal-title">Ajouter un département</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
{{ form_departement.as_p }}
</div>
<div class="modal-footer">
<button type="submit" class="btn bg-orange-dark">Enregistrer</button>
</div>
</div>
</form>
</div>
</div>
<!-- Liste des départements -->
<ul class="list-group">
{% for d in departements %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ d.nom }}
<div>
<!-- Bouton détails -->
<button class="btn btn-sm btn-outline-info" data-bs-toggle="modal" data-bs-target="#modalDetailDepartement{{ d.id }}">
<i class="bi bi-eye"></i> Détails
</button>
<!-- Bouton modification -->
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#modalModifierDepartement{{ d.id }}">
<i class="bi bi-pencil-square"></i>
</button>
<form method="POST" style="display:inline;">
{% csrf_token %}
<input type="hidden" name="supprimer_departement" value="1">
<input type="hidden" name="departement_id" value="{{ d.id }}">
<button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('Voulez-vous vraiment supprimer ce département ?');">
<i class="bi bi-trash"></i>
</button>
</form>
</div>
</li><!-- Modal détails -->
<div class="modal fade" id="modalDetailDepartement{{ d.id }}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-orange-dark text-white">
<h5 class="modal-title">Détails du département</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p><strong>Nom :</strong> {{ d.nom }}</p>
<p><strong>Chef :</strong> {% if d.chef %}{{ d.chef.first_name }} {{ d.chef.last_name }}{% else %}Non défini{% endif %}</p>
<!-- Liste des employés du département -->
{% for emp in d.employe_set.all %}
<p>- {{ emp.username }}</p>
{% empty %}
<p>Aucun employé associé</p>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modalModifierDepartement{{ d.id }}" tabindex="-1">
<div class="modal-dialog">
<form method="POST">
{% csrf_token %}
<input type="hidden" name="modifier_departement" value="1">
<input type="hidden" name="departement_id" value="{{ d.id }}">
<div class="modal-content">
<div class="modal-header bg-orange-dark text-white">
<h5 class="modal-title">Modifier le département</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Nom du département -->
<div class="mb-3">
<label for="nom_{{ d.id }}" class="form-label">Nom du département</label>
<input type="text" class="form-control" id="nom_{{ d.id }}" name="nom" value="{{ d.nom }}" required>
</div>
<!-- Sélection du chef -->
<div class="mb-3">
<label for="chef_{{ d.id }}" class="form-label">Chef du département</label>
<select class="form-select" id="chef_{{ d.id }}" name="chef">
<option value="">-- Aucun chef --</option>
{% for emp in employes %}
<option value="{{ emp.id }}" {% if d.chef and emp.id == d.chef.id %}selected{% endif %}>
{{ emp.first_name }} {{ emp.last_name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn bg-orange-dark">Modifier</button>
</div>
</div>
</form>
</div>
</div>
{% endfor %}
</ul>
</div>
<!-- Colonne Groupes -->
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-2">
<h5><i class="bi bi-person-badge-fill"></i> Groupes et rôles</h5>
<button class="btn bg-orange-dark btn-sm">Ajouter</button>
</div>
<ul class="list-group">
{% for g in groupes %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ g.name }}
<a href="{% url 'admin:auth_group_change' g.id %}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-pencil"></i>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,205 @@
{% load static %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Tableau de Bord RH</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>
<style>
h1 { font-weight: 700; }
.card { border-radius: 15px; }
.chart-container { position: relative; height: 350px; }
table { min-width: 100%; }
/* Couleurs oranges */
.bg-orange-dark { background: linear-gradient(90deg, #b35400, #cc6600); color: white; }
.bg-orange { background: #ff9f1c; color: white; }
.bg-orange-light { background: #ffc107; color: #212529; }
/* Badges */
.badge-warning { background-color: #ffc107; color: #212529; }
.badge-success { background-color: #28a745; color: #fff; }
.badge-primary { background-color: #007bff; color: #fff; }
@media print {
body * { visibility: hidden; }
.printable-table, .printable-table * { visibility: visible; }
.printable-table { position: absolute; top: 0; left: 0; width: 100%; }
}
</style>
</head>
<body>
<div class="container-fluid py-4">
<div class="row flex-nowrap">
{% include 'menu_principal.html' %}
<main class="col px-2 py-4" style="margin-left: 280px;">
<h1 class="mb-5 text-center"><i class="bi bi-bar-chart-line-fill me-2"></i>Tableau de Bord RH</h1>
<!-- Filtres -->
<form method="GET" class="row g-3 mb-5">
<div class="col-md-4">
<select name="mois" class="form-select">
<option value="">Tous les mois</option>
{% for num, nom in mois_liste %}
<option value="{{ num }}" {% if mois_selectionne == num %}selected{% endif %}>{{ nom }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-4">
<select name="departement" class="form-select">
<option value="">Tous les départements</option>
{% for d in departements %}
<option value="{{ d.id }}" {% if departement_selectionne == d.id %}selected{% endif %}>{{ d.nom }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-4">
<button type="submit" class="btn btn-outline-primary w-100"><i class="bi bi-search"></i> Filtrer</button>
</div>
</form>
<!-- Cartes statistiques -->
<div class="row g-4 mb-5">
<div class="col-md-3"><div class="card text-center"><div class="card-body"><h6 class="fw-bold"><i class="bi bi-people me-2"></i>Total Employés</h6><h3 class="fw-bold">{{ total_employes }}</h3></div></div></div>
<div class="col-md-3"><div class="card text-center"><div class="card-body"><h6 class="fw-bold"><i class="bi bi-gender-male me-2"></i>Hommes</h6><h3 class="fw-bold">{{ hommes }}</h3></div></div></div>
<div class="col-md-3"><div class="card text-center"><div class="card-body"><h6 class="fw-bold"><i class="bi bi-gender-female me-2"></i>Femmes</h6><h3 class="fw-bold">{{ femmes }}</h3></div></div></div>
<div class="col-md-3"><div class="card text-center"><div class="card-body"><h6 class="fw-bold"><i class="bi bi-clock-history me-2"></i>Contrats expirants(2mois)</h6><h3 class="fw-bold">{{ expirants }}</h3></div></div></div>
</div>
<div class="row g-4 mb-5">
<div class="col-md-3"><div class="card text-center"><div class="card-header fw-bold"><i class="bi bi-airplane me-2"></i>Congés en attente</div><div class="card-body"><h5 class="display-6 fw-bold">{{ conges_attente }}</h5></div></div></div>
<div class="col-md-3"><div class="card text-center"><div class="card-body"><h6 class="fw-bold"><i class="bi bi-kanban me-2"></i>Projets</h6><h6>(actifs / total)</h6><h3 class="fw-bold">{{ nb_projets_actifs }} / {{ nb_projets }}</h3></div></div></div>
<div class="col-md-3"><div class="card text-center"><div class="card-header fw-bold"><i class="bi bi-clock-history me-2"></i>Âge moyen</div><div class="card-body"><h5 class="display-6 fw-bold">{{ age_moyen }} ans</h5></div></div></div>
</div>
<!-- Graphiques -->
<div class="row g-4 mb-5">
<div class="col-lg-6">
<div class="card shadow"><div class="card-header bg-orange-dark text-white"><i class="bi bi-pie-chart-fill me-2"></i> Répartition par Département</div>
<div class="card-body chart-container"><canvas id="chartDepartement"></canvas></div></div>
</div>
<div class="col-lg-6">
<div class="card shadow"><div class="card-header bg-orange-dark text-white"><i class="bi bi-pie-chart-fill me-2"></i> Répartition par genre</div>
<div class="card-body chart-container"><canvas id="chartSexe"></canvas></div></div>
</div>
</div>
<div class="row g-4 mb-6">
<div class="col-lg-6">
<div class="card shadow"><div class="card-header bg-orange-dark text-white"><i class="bi bi-bar-chart-fill me-2"></i> Projets (En cours / Terminés)</div>
<div class="card-body chart-container"><canvas id="chartProjets"></canvas></div></div>
</div>
<div class="col-lg-6">
<div class="card shadow"><div class="card-header bg-orange-dark text-white"><i class="bi bi-bar-chart-fill me-2"></i> Projets par domaine de recherche</div>
<div class="card-body chart-container"><canvas id="chartDomaine"></canvas></div></div>
</div>
</div>
</main>
</div>
</div>
<script>
const couleursOrange = ['#b35400', '#ff9f1c', '#ffc107', '#ffb84d', '#ffcc80'];
const departementLabels = {{ departement_labels|safe }};
const departementCounts = {{ departement_counts|safe }};
const sexeLabels = {{ sexe_labels|safe }};
const sexeCounts = {{ sexe_counts|safe }};
const projetLabels = {{ projet_labels|safe }};
const projetCounts = {{ projet_counts|safe }};
const ticketsLabels = ["Traités", "Non traités"];
const ticketsCounts = [{{ tickets_traite }}, {{ tickets_non_traite }}];
const domaineLabels = {{ domaine_labels|safe }};
const domaineCounts = {{ domaine_counts|safe }};
new Chart(document.getElementById("chartDepartement"), {
type: 'pie',
plugins: [ChartDataLabels],
data: {
labels: departementLabels,
datasets: [{ data: departementCounts, backgroundColor: couleursOrange }]
},
options: {
plugins: {
legend: { position: 'bottom' },
datalabels: { color: '#fff', font: { weight: 'bold', size: 14 }, formatter: v => v }
}
}
});
new Chart(document.getElementById("chartSexe"), {
type: 'doughnut',
plugins: [ChartDataLabels],
data: {
labels: sexeLabels,
datasets: [{ data: sexeCounts, backgroundColor: couleursOrange }]
},
options: {
plugins: {
legend: { position: 'bottom' },
datalabels: { color: '#fff', font: { weight: 'bold', size: 14 }, formatter: v => v }
}
}
});
new Chart(document.getElementById("chartProjets"), {
type: 'bar',
plugins: [ChartDataLabels],
data: {
labels: projetLabels,
datasets: [{ label:'Nombre de projets', data: projetCounts, backgroundColor: couleursOrange }]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
datalabels: { anchor:'end', align:'top', color:'#000', font:{ weight:'bold', size:16 }, formatter:v => v }
},
scales: {
y: { beginAtZero:true, ticks:{ stepSize:1, precision:0 }, title:{ display:true, text:'Nombre de projets' } },
x: { title:{ display:true, text:'' } }
}
}
});
new Chart(document.getElementById("chartDomaine"), {
type: 'bar',
plugins: [ChartDataLabels],
data: {
labels: domaineLabels,
datasets: [{ label:'Nombre de projets', data: domaineCounts, backgroundColor: couleursOrange }]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
datalabels: { anchor:'end', align:'top', color:'#000', font:{ weight:'bold', size:14 }, formatter:v => v }
},
scales: {
y: { beginAtZero:true, title:{ display:true, text:'Nombre de projets' }, ticks:{ precision:0, stepSize:1 } },
x: {
ticks:{ autoSkip:false, maxRotation:100, minRotation:30, callback: function(value){ return this.getLabelForValue(value); } }
}
}
}
});
document.getElementById('btnVoirRapport').addEventListener('click', function() {
const projetId = document.getElementById('projetId').value;
if(!projetId) return alert("Veuillez sélectionner un projet.");
fetch(`/rapports/projet/${projetId}/`)
.then(resp => { if(!resp.ok) throw new Error("Projet non trouvé"); return resp.text(); })
.then(html => { document.getElementById('contenuRapportProjet').innerHTML = html; })
.catch(err => { document.getElementById('contenuRapportProjet').innerHTML = "<p class='text-danger'>Erreur lors du chargement.</p>"; console.error(err); });
});
</script>
</body>
</html>

View File

View File

@@ -0,0 +1,33 @@
{% load static %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Mot de passe oublié</title>
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'styles.css' %}">
</head>
<body class="container mt-5">
<div class="p-4 shadow-sm rounded bg-white" style="max-width: 400px; margin:auto;">
<h2 class="mb-4 text-center" style="color:rgba(255, 157, 0, 0.895);">🔑 Réinitialiser le mot de passe</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-info">{{ message }}</div>
{% endfor %}
{% endif %}
<form method="POST">
{% csrf_token %}
<div class="mb-3">
<label for="email">Adresse e-mail</label>
<input type="email" name="email" class="form-control" placeholder="Entrez votre email" required>
</div>
<button type="submit" class="btn btn-primary w-100">Envoyer le lien</button>
</form>
<p class="mt-3 text-center">
<a href="{% url 'login' %}">Retour à la connexion</a>
</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,620 @@
{% load static %}
{% load roles %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mes demandes</title>
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'styles.css' %}">
<style>
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
.bg-orange-dark {
background: linear-gradient(90deg, #b35400, #cc6600);
color: white;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row flex-nowrap">
<nav class="col-12 col-md-3 col-lg-2 sidebar p-3">
{% include 'menu_principal.html' %}
</nav>
<main class="col px-4 py-4">
<!-- Boutons Notifications -->
{% if notifications %}
<div class="d-flex justify-content-end mb-3">
<button type="button" class="btn position-relative" data-bs-toggle="modal" data-bs-target="#modalNotifications">
<i class="bi bi-bell-fill"></i> Notifications
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ notifications|length }}
</span>
</button>
</div>
{% endif %}
<!-- Bouton Notifications -->
{% if notifications_employe %}
<div class="d-flex justify-content-end mb-3">
<button type="button" class="btn position-relative" data-bs-toggle="modal" data-bs-target="#modalNotifications">
<i class="bi bi-bell-fill"></i> Notifications
<span id="badgeNotifications" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ notifications_employe|length }}
</span>
</button>
</div>
{% endif %}
<!-- Modal Notifications -->
<div class="modal fade" id="modalNotifications" tabindex="-1" aria-labelledby="modalNotificationsLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalNotificationsLabel">Mes Notifications</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body">
{% if notifications_employe %}
<ul class="list-group">
{% for notif in notifications_employe %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ notif.message|default:"Nouvelle notification" }}
<small class="text-muted">{{ notif.date_created|date:"d/m/Y H:i" }}</small>
</li>
{% endfor %}
</ul>
{% else %}
<p class="text-muted">Aucune notification pour le moment.</p>
{% endif %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
<script>document.addEventListener('DOMContentLoaded', function () {
var modal = document.getElementById('modalNotifications');
modal.addEventListener('show.bs.modal', function () {
fetch("{% url 'notifications-lues' %}", {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token }}',
'Content-Type': 'application/json'
},
body: JSON.stringify({})
})
.then(res => res.json())
.then(data => {
if(data.status === "ok"){
document.getElementById('badgeNotifications').style.display = 'none';
}
});
});
});
</script>
<h1 class="mb-4">La liste des demandes de congés</h1>
<!-- Cartes Statistiques -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-white ">
<div class="card-body">
<h6 class="card-title"><i class="bi bi-list-check me-2"></i> Total Demandes</h6>
<p class="card-text fs-4">{{ total }}</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white ">
<div class="card-body">
<h6 class="card-title"><i class="bi bi-check-circle me-2"></i> Validés</h6>
<p class="card-text fs-4">{{ valides }}</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white ">
<div class="card-body">
<h6 class="card-title"><i class="bi bi-x-circle me-2"></i> Refusés</h6>
<p class="card-text fs-4">{{ refuses }}</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-dark ">
<div class="card-body">
<h6 class="card-title"><i class="bi bi-hourglass-split me-2"></i> En attente</h6>
<p class="card-text fs-4">{{ attentes }}</p>
</div>
</div>
</div>
</div>
{% if messages %}
<!-- Modal message -->
<div class="modal fade" id="messageModal" tabindex="-1" aria-labelledby="messageModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-orange-dark
{% if messages.0.tags == 'error' %}bg-danger text-white
{% elif messages.0.tags == 'success' %}bg-success text-white
{% else %}bg-info text-white{% endif %}">
<h5 class="modal-title" id="messageModalLabel">
{% if messages.0.tags == 'error' %}Erreur
{% elif messages.0.tags == 'success' %}Succès
{% else %}Information{% endif %}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
var messageModal = new bootstrap.Modal(document.getElementById('messageModal'));
messageModal.show();
});
</script>
{% endif %}
<!-- Bouton Nouvelle Demande -->
<div class="d-flex justify-content-between align-items-center mb-3">
<h2><i class="bi bi-people"></i> Liste</h2>
<button type="button" class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#modalDemandeConge">
<i class="bi bi-calendar-plus"></i> Nouvelle demande de congé
</button>
</div>
<!-- Modal Nouvelle Demande -->
<div class="modal fade" id="modalDemandeConge" tabindex="-1" aria-labelledby="modalDemandeCongeLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="modal-header bg-orange-dark">
<h5 class="modal-title" id="modalDemandeCongeLabel">
<i class="bi bi-calendar-plus me-2"></i> Nouvelle demande de congé
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body">
<fieldset class="border p-3 rounded">
<legend class="fw-bold">Informations du congé
Il vous reste {{ employe.solde_conge }} jours de congé Annuel
</legend>
<div class="row">
<div class="col-md-6 mb-3">
{{ form.type.label_tag }} {{ form.type }}
</div>
<div class="col-md-6 mb-3">
{{ form.date_debut.label_tag }} {{ form.date_debut }}
</div>
<div class="col-md-6 mb-3">
{{ form.date_fin.label_tag }} {{ form.date_fin }}
</div>
<div class="col-md-6 mb-3">
{{ form.nombre_jours.label_tag }} {{ form.nombre_jours }}
</div>
</div>
</fieldset>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">
<i class="bi bi-send"></i> Envoyer
</button>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle"></i> Annuler
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Tableau des demandes -->
<table class="table table-striped mt-2">
<thead>
<tr>
<th>Employe</th>
<th>Date demande</th>
<th>Type</th>
<th>Date début</th>
<th>Date fin</th>
<th>Nombre jours</th>
<th>Jours restant</th>
<th>Statut</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for conge in conges %}
<tr>
<td>{{ conge.employe.first_name }} {{ conge.employe.last_name }}</td>
<td>{{ conge.date_demande|date:"d/m/Y" }}</td>
<td>{{ conge.type }}</td>
<td>{{ conge.date_debut|date:"d/m/Y"}}</td>
<td>{{ conge.date_fin|date:"d/m/Y" }}</td>
<td>{{ conge.nombre_jours }}</td>
<td>{{ conge.employe.solde_conge }} </td>
<td>
{% if conge.statut == "Refusé" %}
<span class="badge bg-danger">Refusé</span>
{% elif conge.statut == "Validé directeur" %}
<span class="badge bg-success">Valide </span>
{% elif conge.statut == "Validé chef" %}
<span class="badge bg-warning">Approuvé </span>
{% elif conge.statut == "Refusé par chef" %}
<span class="badge bg-danger">Refusé </span>
{% elif conge.statut == "En attente" %}
<span class="badge bg-warning">En attente </span>
{% endif %}
</td>
<td>
<button class="btn btn-sm btn-info"
data-bs-toggle="modal"
data-bs-target="#detailsCongeModal{{ conge.id }}">
<i class="bi bi-eye"></i> Voir
</button>
{% if conge.statut == "En attente" and conge.employe == request.user %}
<!-- Bouton modifier -->
<button class="btn btn-sm btn-primary"
data-bs-toggle="modal"
data-bs-target="#modifierCongeModal{{ conge.id }}">
Modifier
</button>
<!-- Bouton supprimer -->
<button class="btn btn-sm btn-danger"
data-bs-toggle="modal"
data-bs-target="#supprimerCongeModal{{ conge.id }}">
Supprimer
</button>
{% endif %}
</td>
{% for conge in conges %}
<div class="modal fade" id="modifierCongeModal{{ conge.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post">
{% csrf_token %}
<input type="hidden" name="conge_id" value="{{ conge.id }}">
<div class="modal-header bg-orange-dark">
<h5 class="modal-title">Modifier la demande de congé</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-2">
<label>Type de congé</label>
<select name="type" class="form-select">
<option value="Conge Annuel" {% if conge.type == "Conge Annuel" %}selected{% endif %}>Conge Annuel</option>
<option value="Maladie" {% if conge.type == "Maladie" %}selected{% endif %}>Maladie</option>
<option value="Vacances" {% if conge.type == "Vacances" %}selected{% endif %}>Vacances</option>
<option value="Maternité" {% if conge.type == "Maternité" %}selected{% endif %}>Maternité</option>
</select>
</div>
<div class="mb-2">
<label>Nombre de jours</label>
<input type="number" name="nombre_jours" class="form-control" value="{{ conge.nombre_jours }}">
</div>
<div class="mb-2">
<label>Date début</label>
<input type="date" name="date_debut" class="form-control" value="{{ conge.date_debut|date:'Y-m-d' }}">
</div>
<div class="mb-2">
<label>Date fin</label>
<input type="date" name="date_fin" class="form-control" value="{{ conge.date_fin|date:'Y-m-d' }}">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
<button type="submit" class="btn btn-primary">Enregistrer</button>
</div>
</form>
</div>
</div>
</div>
{% endfor %}
{% for conge in conges %}
<div class="modal fade" id="detailsCongeModal{{ conge.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-orange-dark">
<h5 class="modal-title">Détails de la demande</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<ul class="list-group list-group-flush">
<li class="list-group-item"><strong>Employé :</strong> {{ conge.employe.first_name }} {{ conge.employe.last_name }}</li>
<li class="list-group-item"><strong>Type de congé :</strong> {{ conge.type }}</li>
<li class="list-group-item"><strong>Date début :</strong> {{ conge.date_debut|date:"d/m/Y" }}</li>
<li class="list-group-item"><strong>Date fin :</strong> {{ conge.date_fin|date:"d/m/Y" }}</li>
<li class="list-group-item"><strong>Nombre de jours :</strong> {{ conge.nombre_jours }}</li>
<li class="list-group-item"><strong>Solde restant :</strong> {{ conge.employe.solde_conge }}</li>
<li class="list-group-item">
<strong>Statut :</strong>
{% if conge.statut == "En attente" %}
<span class="badge bg-warning text-dark">{{ conge.statut }}</span>
{% elif conge.statut == "Validé directeur" %}
<span class="badge bg-success">Validé</span>
{% elif conge.statut == "Refusé" %}
<span class="badge bg-danger">Refusé</span>
{% else %}
<span class="badge bg-secondary">{{ conge.statut }}</span>
{% endif %}
</li>
<li class="list-group-item"><strong>Motif :</strong> {{ conge.motif_refus|default:"—" }}</li>
<li class="list-group-item"><strong>Date de la demande :</strong> {{ conge.date_demande|date:"d/m/Y H:i" }}</li>
</ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
{% endfor %}
{% for conge in conges %}
<!-- Modal supprimer -->
<div class="modal fade" id="supprimerCongeModal{{ conge.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post">
{% csrf_token %}
<input type="hidden" name="delete_id" value="{{ conge.id }}">
<div class="modal-header bg-orange-dark">
<h5 class="modal-title">Supprimer la demande</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Voulez-vous vraiment supprimer la demande de congé du
<strong>{{ conge.date_debut|date:"d/m/Y" }}</strong> au
<strong>{{ conge.date_fin|date:"d/m/Y" }}</strong> ?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
<button type="submit" class="btn btn-danger">Supprimer</button>
</div>
</form>
</div>
</div>
</div>
{% endfor %}
</tr>
{% empty %}
<tr>
<td colspan="10" class="text-center text-muted">Aucune demande trouvée.</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Pagination -->
<nav>
<ul class="pagination">
{% for page_num in conges.paginator.page_range %}
<li class="page-item {% if conges.number == page_num %}active{% endif %}">
<a class="page-link" href="?page={{ page_num }}&search={{ search }}">{{ page_num }}</a>
</li>
{% endfor %}
</ul>
</nav>
<!-- Modal Notifications Chef -->
<div class="modal fade" id="modalNotifications" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-orange-dark">
<h5 class="modal-title"><i class="bi bi-bell-fill me-2"></i> Demandes de congé en attente</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
{% if notifications %}
<table class="table table-bordered table-hover">
<thead class="table-light">
<tr>
<th>Employé</th>
<th>Période</th>
<th>Jours</th>
<th>Type</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for conge in notifications %}
<tr>
<td>{{ conge.employe.first_name }} {{ conge.employe.last_name }}</td>
<td>{{ conge.date_debut|date:"d/m/Y" }} - {{ conge.date_fin|date:"d/m/Y" }}</td>
<td>{{ conge.nombre_jours }}</td>
<td>{{ conge.type }}</td>
<td class="d-flex gap-1">
<!-- Valider -->
<form method="POST" action="{% url 'valider-par-chef' conge.id %}">
{% csrf_token %}
<button type="submit" class="btn btn-success btn-sm" title="Valider">
<i class="bi bi-check-circle"></i>
</button>
</form>
<!-- Refuser -->
<button class="btn btn-danger btn-sm btn-refuser"
data-id="{{ conge.id }}"
data-nom="{{ conge.employe.first_name }} {{ conge.employe.last_name }}"
data-bs-toggle="modal"
data-bs-target="#modalRefuserGlobal">
<i class="bi bi-x-circle"></i>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="alert alert-info">Aucune demande en attente.</div>
{% endif %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
<!-- Modal Refuser -->
<div class="modal fade" id="modalRefuserGlobal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" id="formRefuser">
{% csrf_token %}
<div class="modal-header bg-orange-dark">
<h5 class="modal-title">Refuser la demande</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p id="texteConge"></p>
<div class="mb-3">
<label class="form-label">Motif du refus</label>
{{ form.motif_refus }}
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger">Refuser</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const modal = new bootstrap.Modal(document.getElementById('modalRefuserGlobal'));
const texteConge = document.getElementById('texteConge');
const formRefuser = document.getElementById('formRefuser');
document.querySelectorAll('.btn-refuser').forEach(button => {
button.addEventListener('click', function() {
const congeId = this.getAttribute('data-id');
const nomEmploye = this.getAttribute('data-nom');
texteConge.innerHTML = `Refuser le congé de <strong>${nomEmploye}</strong> ?`;
formRefuser.action = `/refuser-par-chef/${congeId}/`; // URL dynamique vers Django
formRefuser.reset();
modal.show();
});
});
});
</script>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('{% static "sw.js" %}').then(function(reg) {
console.log('Service worker registered.', reg);
}).catch(function(err) {
console.warn('Service worker registration failed:', err);
});
});
}
</script>

View File

@@ -0,0 +1,31 @@
{% load static %}
{% load tags_personnaliser %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link href="https://unpkg.com/tabulator-tables@6.4.0/dist/css/tabulator_bootstrap5.min.css" rel="stylesheet">
{% block 'css' %}{% endblock %}
<title> {% block 'titre_page'%}{% endblock%}</title>
</head>
<body>
<div class="container-fluid">
<div class="row flex-nowrap">
{% include 'parts/menu_principal.html' %}
<div class="col-9 p-4">
{% block 'contenu' %} {% endblock %}
</div>
</div>
{% block 'modal' %} {% endblock %}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/3.7.2/luxon.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.8/js/bootstrap.min.js"></script>
{% block 'js' %} {% endblock%}
</body>
</html>

View File

@@ -0,0 +1,43 @@
{% load static %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<title>Login - SIRH</title>
</head>
<body>
<div class="container-fluid vh-100">
<div class="row">
<div class="col-6 vh-100 d-flex flex-column justify-content-center align-items-center">
<img src="{% static 'img/cerfig.jpg' %}" class="w-50">
<h5 class="text-center">Bienvenue sur les systèmes de gestion du CERFIG</h5>
</div>
<div class="col-6 vh-100 d-flex justify-content-center align-items-center">
<form method="POST" action="{% url 'login' %}" class="w-100 shadow rounded px-3 py-5">
<h2 class="text-center">Connexion</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{% if message.tags == 'error' %}danger{% else %}success{% endif %}">{{message}}</div>
{% endfor %}
{% endif %}
{% csrf_token %}
<div class="mb-3">
<label for="mail">Votre adresse email :</label>
<input type="text" name="mail" class="form-control" placeholder="Entrez votre e-mail" required>
</div>
<div class="mb-3">
<label for="mot_de_passe">Mot de passe</label>
<input type="password" name="mot_de_passe" class="form-control" placeholder="Entrez votre mot de passe" required>
<i class="bi bi-eye toggle-password" onclick="togglePassword()"
style="position:absolute; right:10px; top:38px; cursor:pointer;"></i>
</div>
<button type="submit" class="btn btn-primary w-100">Se connecter</button>
</form>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,56 @@
{% load static %}
{% load tags_personnaliser %}
<div class="col-3 bg-danger d-flex flex-column vh-100 pt-5 sticky-top">
<div class="text-center mb-4">
{% if user.employe.photo %}
<img src="{{ user.employe.photo.url }}"
class="rounded-circle"
width="80"
height="80"
style="object-fit:cover;">
{% else %}
<i class="bi bi-person-circle text-white" style="font-size:60px;"></i>
{% endif %}
<div class="text-white mt-2">
{{ user.username }}
</div>
</div>
<a href="{% url 'gestion_employe:mon-profil' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-person-circle"></i> Mon profil
</a>
{% if user|has_group:"ressource_humaine" or user|has_group:"direction" %}
<a href="{% url 'gestion_employe:index' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-speedometer2"></i> Tableau de bord
</a>
{% endif %}
{% if user|has_group:"ressource_humaine" or user|has_group:"direction" %}
<a href="{% url 'gestion_projet:index' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-folder"></i> Gestion des Projets
</a>
{% endif %}
<a href="{% url 'gestion_conges:conge' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-airplane"></i> Gestion des congés
</a>
{% if user|is_chef_projet %}
<a href="{% url 'gestion_projet:activites-projet' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-list-task"></i> Suivi des Activités
</a>
{% endif %}
<a href="{% url 'gestion_salle:reservation-salle' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-calendar-check"></i> Réservation
</a>
{% comment %} <a href="{% url 'rapport-rh' %}" class="text-white fw-bold text-decoration-none mb-2" style="font-size:1.2em">
<i class="bi bi-graph-up"></i> Rapports et Statistiques
</a> {% endcomment %}
{% comment %} <a href="" class="text-white fw-bold text-decoration-none mb-2" style="font-size:1.2em">
<i class="bi bi-person-gear"></i> Gestion des Utilisateurs
</a> {% endcomment %}
{% comment %} <a href="{% url 'gestion_employe:departement' %}" class="text-white fw-bold text-decoration-none mb-2" style="font-size:1.2em">
<i class="bi bi-gear"></i> Paramètres
</a> {% endcomment %}
<a href="{% url 'deconnexion' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-box-arrow-right"></i> Déconnexion
</a>
</div>

67
SIRH/urls.py Normal file
View File

@@ -0,0 +1,67 @@
"""
URL configuration for SIRH project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
from django.conf.urls.static import static
from django.conf import settings
# from simple_sso.sso_server.server import Server
from . import views
# server_sso = Server()
urlpatterns = [
path(
'',
views.login_view,
name='index'
),
path('login/',
views.login_view,
name='login'
),
path(
'deconnexion/',
views.deconnexion_view,
name='deconnexion'
),
path(
'employé/',
include("gestion_employe.urls")
),
path(
'gestion-conge/',
include("gestion_conge.urls")
),
path(
'gestion-projet/',
include("gestion_projet.urls")
),
path(
'gestion-salle/',
include("gestion_salle.urls")
),
path(
'admin/',
admin.site.urls
),
# path(
# 'sso',
# include(server_sso.get_urls())
# )
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

37
SIRH/views.py Normal file
View File

@@ -0,0 +1,37 @@
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect
from django.contrib import messages
def login_view(request):
"""
Gère la connexion des utilisateurs avec redirection selon le rôle et
vérification de l'acceptation de la politique d'utilisation.
"""
if request.method == 'POST':
email = request.POST.get('mail')
password = request.POST.get('mot_de_passe')
if not (email and password):
messages.error(request, "Veuillez remplir tous les champs.")
return render(request, 'login.html')
user = authenticate(request, username=email, password=password)
if user is None:
messages.error(request, "Nom dutilisateur ou mot de passe incorrect.")
return render(request, 'login.html')
if not user.is_active:
messages.error(request, "Compte inactif. Contactez l'administrateur.")
return render(request, 'login.html')
login(request, user)
return redirect("gestion_conges:conge")
return render(request, 'login.html')
def deconnexion_view(request):
"""Gère la déconnexion de l'utilisateur."""
logout(request)
return redirect('login')

16
SIRH/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for SIRH project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'SIRH.settings')
application = get_wsgi_application()