Validation
This commit is contained in:
parent
619efb6238
commit
2304800407
|
|
@ -1,738 +1,163 @@
|
||||||
/* ============================================
|
/* ============================================
|
||||||
STYLE OFFICE POUR LE PORTAIL RH - VERSION STRUCTURE
|
AJOUTS POUR MENU UNIQUE OUVERT
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
:root {
|
/* Animation pour les sous-menus */
|
||||||
/* PowerPoint/Office 2019 Color Palette */
|
@keyframes slideDown {
|
||||||
--office-primary: #b7472a; /* Sidebar color - PowerPoint accent */
|
from {
|
||||||
--office-secondary: #2b579a; /* Header/buttons - Office blue */
|
opacity: 0;
|
||||||
--office-accent: #107c10; /* Success/active states - Excel green */
|
transform: translateY(-10px);
|
||||||
--office-light: #f3f2f1; /* Background - Office gray */
|
max-height: 0;
|
||||||
--office-common: #e6e6e6; /* Background - Office gray */
|
}
|
||||||
--office-dark: #323130; /* Text color */
|
to {
|
||||||
--office-border: #d0d0d0; /* Borders */
|
opacity: 1;
|
||||||
--office-hover: #f0f0f0; /* Hover states */
|
transform: translateY(0);
|
||||||
--office-card: #ffffff; /* Card backgrounds */
|
max-height: 500px;
|
||||||
|
}
|
||||||
/* Dimensions */
|
|
||||||
--sidebar-width: 260px;
|
|
||||||
--sidebar-collapsed: 70px;
|
|
||||||
--header-height: 64px;
|
|
||||||
--transition-speed: 0.3s;
|
|
||||||
|
|
||||||
/* Responsive breakpoints */
|
|
||||||
--breakpoint-tablet: 1200px;
|
|
||||||
--breakpoint-mobile: 768px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
@keyframes slideUp {
|
||||||
BASE STYLES & RESET
|
from {
|
||||||
============================================ */
|
opacity: 1;
|
||||||
* {
|
transform: translateY(0);
|
||||||
margin: 0;
|
max-height: 500px;
|
||||||
padding: 0;
|
}
|
||||||
box-sizing: border-box;
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
/* Styles pour le menu unique ouvert */
|
||||||
font-family: 'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
|
.nav-submenu {
|
||||||
background-color: var(--office-common);
|
max-height: 0;
|
||||||
color: var(--office-dark);
|
|
||||||
line-height: 1.5;
|
|
||||||
overflow-x: hidden;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HEADER STYLES
|
|
||||||
============================================ */
|
|
||||||
.app-header {
|
|
||||||
background-color: var(--office-light) !important;
|
|
||||||
height: var(--header-height);
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 1030;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-logo {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: #313c4c;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-logo img {
|
|
||||||
height: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
SIDEBAR STYLES
|
|
||||||
============================================ */
|
|
||||||
.app-sidebar {
|
|
||||||
width: var(--sidebar-width);
|
|
||||||
background: linear-gradient(180deg, var(--office-primary) 0%, #a53e24 100%);
|
|
||||||
position: fixed;
|
|
||||||
top: var(--header-height);
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 1020;
|
|
||||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: width var(--transition-speed) ease;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pattern overlay */
|
|
||||||
.app-sidebar::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-image:
|
|
||||||
radial-gradient(circle at 10% 20%, rgba(255, 255, 255, 0.03) 0%, transparent 20%),
|
|
||||||
radial-gradient(circle at 90% 80%, rgba(255, 255, 255, 0.03) 0%, transparent 20%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-nav {
|
|
||||||
padding: 24px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-section {
|
|
||||||
margin-bottom: 32px;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-title {
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
padding: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: rgba(255, 255, 255, 0.85);
|
|
||||||
padding: 12px 16px;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
animation: slideUp 0.3s ease forwards;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:hover {
|
.nav-submenu.show {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
display: block;
|
||||||
color: white;
|
animation: slideDown 0.3s ease forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active {
|
/* Désactiver la logique Bootstrap par défaut */
|
||||||
background-color: rgba(255, 255, 255, 0.15);
|
.nav-link[data-bs-toggle="collapse"] {
|
||||||
color: white;
|
pointer-events: auto;
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link i {
|
.nav-link[data-bs-toggle="collapse"].collapsed .nav-arrow {
|
||||||
width: 24px;
|
transform: rotate(0deg);
|
||||||
font-size: 18px;
|
|
||||||
margin-right: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-text {
|
.nav-link[data-bs-toggle="collapse"]:not(.collapsed) .nav-arrow {
|
||||||
flex: 1;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-badge {
|
|
||||||
background-color: var(--office-accent);
|
|
||||||
color: white;
|
|
||||||
font-size: 11px;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-arrow {
|
|
||||||
font-size: 12px;
|
|
||||||
opacity: 0.7;
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link[aria-expanded="true"] .nav-arrow {
|
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-submenu {
|
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-top: 4px;
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-submenu .nav-link {
|
|
||||||
padding: 8px 16px 8px 52px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-submenu .nav-link::before {
|
|
||||||
content: '•';
|
|
||||||
position: absolute;
|
|
||||||
left: 36px;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
MAIN CONTENT AREA
|
AMÉLIORATIONS UX
|
||||||
============================================ */
|
============================================ */
|
||||||
.app-main {
|
|
||||||
margin-left: var(--sidebar-width);
|
|
||||||
padding-top: var(--header-height);
|
|
||||||
min-height: 100vh;
|
|
||||||
transition: margin-left var(--transition-speed) ease;
|
|
||||||
background-color: var(--office-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-area {
|
/* Zone de détection de proximité */
|
||||||
padding: 24px;
|
.proximity-hover-area {
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
NAVIGATION TABS
|
|
||||||
============================================ */
|
|
||||||
.nav-bar {
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px 24px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
||||||
border: 1px solid var(--office-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 16px;
|
|
||||||
background-color: transparent;
|
|
||||||
color: var(--office-dark);
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab:hover {
|
|
||||||
background-color: var(--office-hover);
|
|
||||||
color: var(--office-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab.active {
|
|
||||||
background-color: var(--office-secondary);
|
|
||||||
color: white;
|
|
||||||
border-color: var(--office-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab i {
|
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CONTENT CARDS
|
|
||||||
============================================ */
|
|
||||||
.content-card {
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
||||||
border: 1px solid var(--office-border);
|
|
||||||
margin-bottom: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
background-color: #fafafa;
|
|
||||||
border-bottom: 1px solid var(--office-border);
|
|
||||||
padding: 20px 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--office-dark);
|
|
||||||
font-size: 18px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HEADER CONTROLS
|
|
||||||
============================================ */
|
|
||||||
.header-controls {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-btn {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
color: #313c4c;
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-btn:hover {
|
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 2px;
|
|
||||||
right: 2px;
|
|
||||||
background-color: var(--office-accent);
|
|
||||||
color: white;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 600;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-menu {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-menu:hover {
|
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, var(--office-primary), #ff6b35);
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-name {
|
|
||||||
color: #313c4c;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-role {
|
|
||||||
color: #313c4c;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CONTEXT PANEL
|
|
||||||
============================================ */
|
|
||||||
.context-panel {
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: -380px;
|
|
||||||
top: var(--header-height);
|
|
||||||
bottom: 0;
|
|
||||||
width: 380px;
|
|
||||||
background-color: white;
|
|
||||||
box-shadow: -2px 0 12px rgba(0, 0, 0, 0.1);
|
|
||||||
z-index: 1015;
|
|
||||||
transition: right 0.3s ease;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-left: 1px solid var(--office-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-panel.open {
|
|
||||||
right: 0;
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
width: 80px;
|
||||||
|
height: 200px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 1005;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-header {
|
/* État hors ligne */
|
||||||
background: linear-gradient(135deg, var(--office-primary), #ee6a49);
|
body.offline .app-header::after {
|
||||||
|
content: 'Hors ligne';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: #ff6b35;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 20px 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-close {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
font-size: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-close:hover {
|
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-body {
|
|
||||||
flex: 1;
|
|
||||||
padding: 24px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-section {
|
|
||||||
margin-bottom: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--office-secondary);
|
|
||||||
margin-bottom: 12px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title i {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-box {
|
|
||||||
background-color: #fafafa;
|
|
||||||
border: 1px solid var(--office-border);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 16px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-label {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-value {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--office-dark);
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid var(--office-border);
|
|
||||||
border-radius: 6px;
|
|
||||||
color: var(--office-dark);
|
|
||||||
font-weight: 500;
|
|
||||||
text-align: left;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:hover {
|
|
||||||
background-color: var(--office-hover);
|
|
||||||
border-color: var(--office-secondary);
|
|
||||||
color: var(--office-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn i {
|
|
||||||
width: 20px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-container {
|
|
||||||
text-align: center;
|
|
||||||
padding: 16px;
|
|
||||||
background-color: #fafafa;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--office-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-photo {
|
|
||||||
width: 150px;
|
|
||||||
height: 150px;
|
|
||||||
border-radius: 8px;
|
|
||||||
object-fit: cover;
|
|
||||||
border: 3px solid white;
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-photo:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CONTEXT TOGGLE BUTTON
|
|
||||||
============================================ */
|
|
||||||
.context-toggle {
|
|
||||||
position: fixed;
|
|
||||||
right: 24px;
|
|
||||||
bottom: 24px;
|
|
||||||
width: 56px;
|
|
||||||
height: 56px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, var(--office-primary), #ff6b35);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
box-shadow: 0 4px 12px rgba(183, 71, 42, 0.3);
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 20px;
|
|
||||||
z-index: 1010;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-toggle:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 6px 16px rgba(183, 71, 42, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
TEST MODE INDICATOR
|
|
||||||
============================================ */
|
|
||||||
.test-indicator {
|
|
||||||
background: linear-gradient(45deg, #ffd700, #ffed4e);
|
|
||||||
color: #8a6d3b;
|
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
margin-left: 12px;
|
|
||||||
animation: pulse 2s infinite;
|
animation: pulse 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
/* Spinner amélioré */
|
||||||
0% { opacity: 1; }
|
.spinner-responsive {
|
||||||
50% { opacity: 0.7; }
|
|
||||||
100% { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
LANGUAGE SELECTOR
|
|
||||||
============================================ */
|
|
||||||
.language-selector {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
min-height: 300px;
|
||||||
padding: 6px 12px;
|
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
color: #313c4c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.language-selector:hover {
|
.spinner-responsive i {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
font-size: 48px;
|
||||||
|
color: var(--office-primary);
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.language-flag {
|
@keyframes spin {
|
||||||
width: 24px;
|
0% { transform: rotate(0deg); }
|
||||||
height: 16px;
|
100% { transform: rotate(360deg); }
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
MODAL CUSTOMIZATION
|
RESPONSIVE AMÉLIORÉ
|
||||||
============================================ */
|
============================================ */
|
||||||
.modal-office .modal-header {
|
|
||||||
background: linear-gradient(135deg, var(--office-secondary), #1e4a8b);
|
|
||||||
color: white;
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-office .modal-title {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
RESPONSIVE DESIGN
|
|
||||||
============================================ */
|
|
||||||
@media (max-width: 1200px) {
|
|
||||||
.app-sidebar {
|
|
||||||
width: var(--sidebar-collapsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-main {
|
|
||||||
margin-left: var(--sidebar-collapsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-text,
|
|
||||||
.nav-title,
|
|
||||||
.nav-badge,
|
|
||||||
.nav-arrow {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
justify-content: center;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link i {
|
|
||||||
margin-right: 0;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-submenu .nav-link {
|
|
||||||
padding: 12px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-submenu .nav-link::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-panel {
|
|
||||||
width: 100%;
|
|
||||||
right: -100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.app-sidebar {
|
.app-sidebar {
|
||||||
transform: translateX(-100%);
|
transition: transform 0.3s ease;
|
||||||
width: var(--sidebar-width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-sidebar.open {
|
.app-sidebar.show {
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
|
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-main {
|
.context-toggle {
|
||||||
margin-left: 0;
|
width: 48px;
|
||||||
}
|
height: 48px;
|
||||||
|
right: 16px;
|
||||||
.header-content {
|
bottom: 16px;
|
||||||
padding: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-area {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-text,
|
|
||||||
.nav-title,
|
|
||||||
.nav-badge,
|
|
||||||
.nav-arrow {
|
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
SCROLLBAR STYLING
|
ACCESSIBILITÉ
|
||||||
============================================ */
|
============================================ */
|
||||||
.app-sidebar::-webkit-scrollbar,
|
|
||||||
.context-body::-webkit-scrollbar {
|
.nav-link:focus {
|
||||||
width: 6px;
|
outline: 2px solid var(--office-secondary);
|
||||||
|
outline-offset: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-sidebar::-webkit-scrollbar-track,
|
.context-toggle:focus {
|
||||||
.context-body::-webkit-scrollbar-track {
|
outline: 3px solid var(--office-secondary);
|
||||||
background: rgba(255, 255, 255, 0.1);
|
outline-offset: 2px;
|
||||||
border-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-sidebar::-webkit-scrollbar-thumb,
|
/* High contrast mode */
|
||||||
.context-body::-webkit-scrollbar-thumb {
|
@media (prefers-contrast: high) {
|
||||||
background: rgba(255, 255, 255, 0.3);
|
:root {
|
||||||
border-radius: 3px;
|
--office-border: #000000;
|
||||||
|
--office-dark: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-sidebar::-webkit-scrollbar-thumb:hover,
|
.nav-link.active {
|
||||||
.context-body::-webkit-scrollbar-thumb:hover {
|
outline: 3px solid #000;
|
||||||
background: rgba(255, 255, 255, 0.4);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduced motion */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
* {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,558 +1,71 @@
|
||||||
/* ============================================
|
/* ============================================
|
||||||
ENHANCEMENTS UX POUR PORTAIL INTER SANTÉ
|
UX ENHANCEMENTS - ANIMATIONS ET MICRO-INTERACTIONS
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
:root {
|
/* Effet de vague au clic */
|
||||||
/* Variables UX avancées */
|
.ripple {
|
||||||
--ux-transition-smooth: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
--ux-shadow-soft: 0 2px 15px rgba(0, 0, 0, 0.08);
|
|
||||||
--ux-shadow-medium: 0 5px 20px rgba(0, 0, 0, 0.12);
|
|
||||||
--ux-shadow-hard: 0 10px 30px rgba(0, 0, 0, 0.15);
|
|
||||||
--ux-border-radius-sm: 8px;
|
|
||||||
--ux-border-radius-md: 12px;
|
|
||||||
--ux-border-radius-lg: 20px;
|
|
||||||
|
|
||||||
/* Variables d'animation */
|
|
||||||
--animation-duration-fast: 150ms;
|
|
||||||
--animation-duration-normal: 300ms;
|
|
||||||
--animation-duration-slow: 500ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
ENHANCED SIDEBAR UX
|
|
||||||
============================================ */
|
|
||||||
.app-sidebar {
|
|
||||||
--sidebar-hover-glow: 0 0 20px rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
transition: all var(--animation-duration-normal) var(--ux-transition-smooth);
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
will-change: transform, background-color, border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link:hover {
|
|
||||||
background: linear-gradient(90deg,
|
|
||||||
rgba(255, 255, 255, 0.1) 0%,
|
|
||||||
rgba(255, 255, 255, 0.05) 100%);
|
|
||||||
transform: translateX(8px);
|
|
||||||
border-left-color: rgba(255, 255, 255, 0.5);
|
|
||||||
box-shadow: var(--sidebar-hover-glow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link.active {
|
|
||||||
background: linear-gradient(90deg,
|
|
||||||
rgba(255, 255, 255, 0.2) 0%,
|
|
||||||
rgba(255, 255, 255, 0.1) 100%);
|
|
||||||
border-left-color: #ffffff;
|
|
||||||
box-shadow: inset 0 0 30px rgba(255, 255, 255, 0.1),
|
|
||||||
0 0 20px rgba(255, 255, 255, 0.15);
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Indicateur visuel pour lien actif */
|
|
||||||
.nav-link.active::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: -3px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 0 10px white;
|
|
||||||
animation: pulse-active 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse-active {
|
|
||||||
0%, 100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(-50%) scale(1);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0.7;
|
|
||||||
transform: translateY(-50%) scale(1.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sous-menus améliorés */
|
|
||||||
.nav-submenu {
|
|
||||||
background: linear-gradient(180deg,
|
|
||||||
rgba(0, 0, 0, 0.15) 0%,
|
|
||||||
rgba(0, 0, 0, 0.1) 100%);
|
|
||||||
border-radius: var(--ux-border-radius-sm);
|
|
||||||
margin: 6px 12px;
|
|
||||||
border-left: 2px solid rgba(255, 255, 255, 0.2);
|
|
||||||
transition: all 0.4s var(--ux-transition-smooth);
|
|
||||||
max-height: 0;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-submenu.show {
|
.ripple::after {
|
||||||
max-height: 500px;
|
|
||||||
padding: 8px 0;
|
|
||||||
margin-top: 8px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
animation: slideDown 0.4s var(--ux-transition-smooth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideDown {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
max-height: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
max-height: 500px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CONTEXT BUTTON - QUILLBOT STYLE
|
|
||||||
============================================ */
|
|
||||||
.context-toggle {
|
|
||||||
position: fixed;
|
|
||||||
right: 30px;
|
|
||||||
bottom: 30px;
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, #b7472a 0%, #ff6b35 100%);
|
|
||||||
color: white;
|
|
||||||
border: 3px solid white;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 24px;
|
|
||||||
z-index: 1050;
|
|
||||||
transition: all 0.5s var(--ux-transition-smooth);
|
|
||||||
opacity: 0.2;
|
|
||||||
transform: scale(0.8);
|
|
||||||
box-shadow:
|
|
||||||
0 4px 25px rgba(183, 71, 42, 0.15),
|
|
||||||
inset 0 0 20px rgba(255, 255, 255, 0.2);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
will-change: transform, opacity, box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animation d'attente */
|
|
||||||
.context-toggle::after {
|
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 72px;
|
top: 50%;
|
||||||
height: 72px;
|
left: 50%;
|
||||||
border: 2px solid rgba(183, 71, 42, 0.3);
|
width: 5px;
|
||||||
border-radius: 50%;
|
height: 5px;
|
||||||
animation: ripple 3s infinite;
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
opacity: 0;
|
||||||
|
border-radius: 100%;
|
||||||
|
transform: scale(1, 1) translate(-50%);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ripple:focus:not(:active)::after {
|
||||||
|
animation: ripple 1s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ripple {
|
@keyframes ripple {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(0.8);
|
transform: scale(0, 0);
|
||||||
opacity: 1;
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
transform: scale(25, 25);
|
||||||
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1.2);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
transform: scale(40, 40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* État au survol et activation */
|
/* Effet de levitation pour les cartes */
|
||||||
.context-toggle:hover,
|
|
||||||
.context-toggle.active,
|
|
||||||
body:has(.proximity-hover-area:hover) .context-toggle {
|
|
||||||
opacity: 1 !important;
|
|
||||||
transform: scale(1.1) !important;
|
|
||||||
box-shadow:
|
|
||||||
0 8px 35px rgba(183, 71, 42, 0.4),
|
|
||||||
0 0 30px rgba(255, 107, 53, 0.3),
|
|
||||||
inset 0 0 25px rgba(255, 255, 255, 0.3);
|
|
||||||
animation: bounce 0.5s var(--ux-transition-smooth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bounce {
|
|
||||||
0%, 100% { transform: scale(1.1); }
|
|
||||||
50% { transform: scale(1.15); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rotation quand le panel est ouvert */
|
|
||||||
.context-panel.open ~ .context-toggle {
|
|
||||||
transform: rotate(180deg) scale(1.1);
|
|
||||||
background: linear-gradient(135deg, #2b579a 0%, #1e4a8b 100%);
|
|
||||||
right: 410px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Zone de détection de proximité invisible */
|
|
||||||
.proximity-hover-area {
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
z-index: 1049;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Feedback visuel pour la zone */
|
|
||||||
.proximity-hover-area::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
right: 30px;
|
|
||||||
bottom: 30px;
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
background: radial-gradient(circle, rgba(183, 71, 42, 0.1) 0%, transparent 70%);
|
|
||||||
border-radius: 50%;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity var(--animation-duration-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.proximity-hover-area:hover::before {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
ENHANCED CONTEXT PANEL
|
|
||||||
============================================ */
|
|
||||||
.context-panel {
|
|
||||||
transition: right var(--animation-duration-slow) var(--ux-transition-smooth);
|
|
||||||
box-shadow:
|
|
||||||
-5px 0 30px rgba(0, 0, 0, 0.15),
|
|
||||||
0 0 0 1px rgba(255, 255, 255, 0.1) inset;
|
|
||||||
border-left: 3px solid var(--office-primary);
|
|
||||||
will-change: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-panel.open {
|
|
||||||
animation: slideInRight 0.5s var(--ux-transition-smooth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideInRight {
|
|
||||||
from {
|
|
||||||
transform: translateX(100px);
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateX(0);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-header {
|
|
||||||
background: linear-gradient(135deg, var(--office-primary) 0%, #d9534f 100%);
|
|
||||||
padding: 25px 30px;
|
|
||||||
border-bottom: 3px solid rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-title {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-close {
|
|
||||||
transition: all var(--animation-duration-normal);
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-close:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sections avec effet de profondeur */
|
|
||||||
.context-section {
|
|
||||||
background: white;
|
|
||||||
border-radius: var(--ux-border-radius-md);
|
|
||||||
padding: 20px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
box-shadow: var(--ux-shadow-soft);
|
|
||||||
border: 1px solid var(--office-border);
|
|
||||||
transition: all var(--animation-duration-normal);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-section:hover {
|
|
||||||
transform: translateY(-3px);
|
|
||||||
box-shadow: var(--ux-shadow-medium);
|
|
||||||
border-color: var(--office-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-section::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 4px;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(to bottom, var(--office-primary), var(--office-secondary));
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
color: var(--office-secondary);
|
|
||||||
font-size: 15px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
border-bottom: 2px solid rgba(43, 87, 154, 0.1);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: -2px;
|
|
||||||
left: 0;
|
|
||||||
width: 50px;
|
|
||||||
height: 2px;
|
|
||||||
background: linear-gradient(90deg, var(--office-primary), var(--office-secondary));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cards améliorées */
|
|
||||||
.info-box {
|
|
||||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
|
||||||
border-radius: var(--ux-border-radius-sm);
|
|
||||||
padding: 18px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.03);
|
|
||||||
transition: all var(--animation-duration-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-box:hover {
|
|
||||||
border-color: var(--office-secondary);
|
|
||||||
box-shadow:
|
|
||||||
inset 0 2px 8px rgba(43, 87, 154, 0.05),
|
|
||||||
0 3px 10px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-value {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--office-dark);
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Boutons d'action améliorés */
|
|
||||||
.action-btn {
|
|
||||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
|
||||||
border: 2px solid var(--office-border);
|
|
||||||
border-radius: var(--ux-border-radius-sm);
|
|
||||||
padding: 15px 20px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
transition: all var(--animation-duration-normal) var(--ux-transition-smooth);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
will-change: transform, border-color, background;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:hover {
|
|
||||||
transform: translateX(8px);
|
|
||||||
border-color: var(--office-secondary);
|
|
||||||
background: linear-gradient(135deg, #f0f7ff 0%, #e3eeff 100%);
|
|
||||||
box-shadow:
|
|
||||||
5px 5px 15px rgba(43, 87, 154, 0.1),
|
|
||||||
inset 0 0 20px rgba(255, 255, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: -100%;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg,
|
|
||||||
transparent,
|
|
||||||
rgba(255, 255, 255, 0.4),
|
|
||||||
transparent);
|
|
||||||
transition: left 0.6s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:hover::before {
|
|
||||||
left: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Photo patient améliorée */
|
|
||||||
.photo-container {
|
|
||||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
|
||||||
border-radius: var(--ux-border-radius-md);
|
|
||||||
padding: 25px;
|
|
||||||
border: 2px dashed var(--office-border);
|
|
||||||
transition: all var(--animation-duration-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-container:hover {
|
|
||||||
border-color: var(--office-primary);
|
|
||||||
background: linear-gradient(135deg, #fff5f2 0%, #ffffff 100%);
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-photo {
|
|
||||||
width: 180px;
|
|
||||||
height: 180px;
|
|
||||||
border-radius: var(--ux-border-radius-md);
|
|
||||||
object-fit: cover;
|
|
||||||
border: 5px solid white;
|
|
||||||
box-shadow:
|
|
||||||
0 10px 30px rgba(0, 0, 0, 0.15),
|
|
||||||
0 0 0 1px rgba(0, 0, 0, 0.05);
|
|
||||||
transition: all 0.4s var(--ux-transition-smooth);
|
|
||||||
cursor: pointer;
|
|
||||||
will-change: transform, box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-photo:hover {
|
|
||||||
transform: scale(1.08) rotate(2deg);
|
|
||||||
box-shadow:
|
|
||||||
0 20px 40px rgba(0, 0, 0, 0.2),
|
|
||||||
0 0 0 3px rgba(183, 71, 42, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HEADER ENHANCEMENTS
|
|
||||||
============================================ */
|
|
||||||
.app-header {
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
background: linear-gradient(135deg,
|
|
||||||
rgba(243, 242, 241, 0.95) 0%,
|
|
||||||
rgba(255, 255, 255, 0.98) 100%);
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
box-shadow:
|
|
||||||
0 4px 20px rgba(0, 0, 0, 0.05),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
background: linear-gradient(135deg, var(--office-primary) 0%, #ff8c69 100%);
|
|
||||||
box-shadow:
|
|
||||||
0 4px 15px rgba(183, 71, 42, 0.3),
|
|
||||||
inset 0 0 20px rgba(255, 255, 255, 0.3);
|
|
||||||
transition: all var(--animation-duration-normal);
|
|
||||||
will-change: transform, box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar:hover {
|
|
||||||
transform: scale(1.1) rotate(5deg);
|
|
||||||
box-shadow:
|
|
||||||
0 6px 25px rgba(183, 71, 42, 0.4),
|
|
||||||
inset 0 0 25px rgba(255, 255, 255, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-badge {
|
|
||||||
background: linear-gradient(135deg, #107c10 0%, #20a020 100%);
|
|
||||||
box-shadow: 0 3px 10px rgba(16, 124, 16, 0.4);
|
|
||||||
animation: pulse-notification 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse-notification {
|
|
||||||
0%, 100% { transform: scale(1); }
|
|
||||||
50% { transform: scale(1.1); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
MAIN CONTENT ENHANCEMENTS
|
|
||||||
============================================ */
|
|
||||||
.app-main {
|
|
||||||
background: linear-gradient(135deg,
|
|
||||||
#f3f2f1 0%,
|
|
||||||
#f8f7f6 30%,
|
|
||||||
#fefefe 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-area {
|
|
||||||
animation: fadeIn 0.6s var(--ux-transition-smooth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-bar {
|
|
||||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
|
||||||
border-radius: var(--ux-border-radius-md);
|
|
||||||
box-shadow: var(--ux-shadow-soft);
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab {
|
|
||||||
transition: all var(--animation-duration-normal);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
will-change: transform, box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 15px rgba(43, 87, 154, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab.active {
|
|
||||||
background: linear-gradient(135deg, var(--office-secondary) 0%, #3a6bc0 100%);
|
|
||||||
box-shadow:
|
|
||||||
0 5px 20px rgba(43, 87, 154, 0.3),
|
|
||||||
inset 0 0 20px rgba(255, 255, 255, 0.2);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
animation: tab-active-pulse 3s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes tab-active-pulse {
|
|
||||||
0%, 100% {
|
|
||||||
box-shadow: 0 5px 20px rgba(43, 87, 154, 0.3);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
box-shadow: 0 5px 25px rgba(43, 87, 154, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card {
|
.content-card {
|
||||||
background: linear-gradient(135deg, #ffffff 0%, #fcfcfc 100%);
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
border-radius: var(--ux-border-radius-md);
|
|
||||||
box-shadow: var(--ux-shadow-soft);
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
||||||
transition: all 0.4s;
|
|
||||||
will-change: transform, box-shadow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card:hover {
|
.content-card:hover {
|
||||||
transform: translateY(-3px);
|
transform: translateY(-4px);
|
||||||
box-shadow: var(--ux-shadow-medium);
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||||
border-color: rgba(43, 87, 154, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* Animation du bouton contexte */
|
||||||
LOADING STATES
|
.context-toggle {
|
||||||
============================================ */
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
.loading-placeholder {
|
}
|
||||||
background: linear-gradient(90deg,
|
|
||||||
#f0f0f0 25%,
|
.context-toggle:hover {
|
||||||
#e0e0e0 50%,
|
transform: scale(1.1) translateY(-2px);
|
||||||
#f0f0f0 75%);
|
box-shadow: 0 8px 20px rgba(183, 71, 42, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading skeleton */
|
||||||
|
.skeleton {
|
||||||
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||||
background-size: 200% 100%;
|
background-size: 200% 100%;
|
||||||
animation: loading 1.5s infinite;
|
animation: loading 1.5s infinite;
|
||||||
border-radius: var(--ux-border-radius-sm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loading {
|
@keyframes loading {
|
||||||
|
|
@ -560,274 +73,66 @@ body:has(.proximity-hover-area:hover) .context-toggle {
|
||||||
100% { background-position: -200% 0; }
|
100% { background-position: -200% 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* Smooth scrolling */
|
||||||
ACCESSIBILITY ENHANCEMENTS
|
html {
|
||||||
============================================ */
|
scroll-behavior: smooth;
|
||||||
|
|
||||||
/* Navigation au clavier */
|
|
||||||
body.keyboard-navigation .nav-link:focus,
|
|
||||||
body.keyboard-navigation .action-btn:focus,
|
|
||||||
body.keyboard-navigation .context-toggle:focus {
|
|
||||||
outline: 3px solid rgba(43, 87, 154, 0.5);
|
|
||||||
outline-offset: 3px;
|
|
||||||
box-shadow: 0 0 0 6px rgba(43, 87, 154, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lien skip pour accessibilité */
|
/* Focus styles améliorés */
|
||||||
.skip-link {
|
:focus-visible {
|
||||||
position: absolute;
|
outline: 3px solid var(--office-secondary);
|
||||||
top: -40px;
|
outline-offset: 2px;
|
||||||
left: 0;
|
border-radius: 4px;
|
||||||
background: var(--office-primary);
|
|
||||||
color: white;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 0 0 4px 0;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
z-index: 9999;
|
|
||||||
transition: top 0.3s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.skip-link:focus {
|
/* Animation d'entrée */
|
||||||
top: 0;
|
.fade-in-up {
|
||||||
outline: 3px solid white;
|
animation: fadeInUp 0.6s ease forwards;
|
||||||
outline-offset: -3px;
|
opacity: 0;
|
||||||
}
|
transform: translateY(20px);
|
||||||
|
|
||||||
/* High contrast mode */
|
|
||||||
@media (prefers-contrast: high) {
|
|
||||||
.nav-link.active {
|
|
||||||
border-left: 4px solid white;
|
|
||||||
background-color: rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-section {
|
|
||||||
border: 2px solid var(--office-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
border: 2px solid currentColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reduced motion */
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
*,
|
|
||||||
*::before,
|
|
||||||
*::after {
|
|
||||||
animation-duration: 0.01ms !important;
|
|
||||||
animation-iteration-count: 1 !important;
|
|
||||||
transition-duration: 0.01ms !important;
|
|
||||||
scroll-behavior: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-toggle::after {
|
|
||||||
animation: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark mode support */
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.app-header {
|
|
||||||
background: rgba(30, 30, 30, 0.95);
|
|
||||||
border-bottom-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card {
|
|
||||||
background: #2a2a2a;
|
|
||||||
border-color: #404040;
|
|
||||||
color: #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-bar {
|
|
||||||
background: #2a2a2a;
|
|
||||||
border-color: #404040;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-box {
|
|
||||||
background: #333;
|
|
||||||
border-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
background: #333;
|
|
||||||
border-color: #444;
|
|
||||||
color: #e0e0e0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
RESPONSIVE ENHANCEMENTS
|
|
||||||
============================================ */
|
|
||||||
@media (max-width: 1200px) {
|
|
||||||
.app-sidebar:hover {
|
|
||||||
width: var(--sidebar-width);
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-sidebar:hover .nav-text,
|
|
||||||
.app-sidebar:hover .nav-arrow {
|
|
||||||
display: block;
|
|
||||||
animation: fadeIn 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-panel {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-panel.open ~ .context-toggle {
|
|
||||||
right: calc(90% + 20px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.context-toggle {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
font-size: 20px;
|
|
||||||
right: 20px;
|
|
||||||
bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-toggle::after {
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-panel {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-panel.open ~ .context-toggle {
|
|
||||||
right: calc(100% - 70px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-bar {
|
|
||||||
padding: 12px 15px;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs {
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tab {
|
|
||||||
padding: 8px 12px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Masquer certains éléments sur mobile */
|
|
||||||
.user-info .user-role {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-indicator span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-indicator {
|
|
||||||
padding: 4px 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.header-content {
|
|
||||||
padding: 0 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-logo span {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-name {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-body {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-section {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-photo {
|
|
||||||
width: 140px;
|
|
||||||
height: 140px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
UTILITY CLASSES
|
|
||||||
============================================ */
|
|
||||||
.visually-hidden {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
padding: 0;
|
|
||||||
margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-balance {
|
|
||||||
text-wrap: balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-pretty {
|
|
||||||
text-wrap: pretty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connection warning */
|
|
||||||
.connection-warning {
|
|
||||||
position: fixed;
|
|
||||||
top: 70px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
z-index: 9999;
|
|
||||||
max-width: 90%;
|
|
||||||
animation: slideDown 0.3s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animation for loaded images */
|
|
||||||
img.loaded {
|
|
||||||
animation: fadeInUp 0.5s ease-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadeInUp {
|
@keyframes fadeInUp {
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(10px);
|
|
||||||
}
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print styles */
|
/* Tooltip personnalisé */
|
||||||
@media print {
|
[data-tooltip] {
|
||||||
.app-sidebar,
|
position: relative;
|
||||||
.app-header,
|
|
||||||
.context-panel,
|
|
||||||
.context-toggle,
|
|
||||||
.nav-bar {
|
|
||||||
display: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-main {
|
[data-tooltip]:hover::before {
|
||||||
margin-left: 0;
|
content: attr(data-tooltip);
|
||||||
padding-top: 0;
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: var(--office-dark);
|
||||||
|
color: white;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 1000;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
animation: fadeIn 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card {
|
[data-tooltip]:hover::after {
|
||||||
box-shadow: none;
|
content: '';
|
||||||
border: 1px solid #ddd;
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border: 6px solid transparent;
|
||||||
|
border-top-color: var(--office-dark);
|
||||||
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
@keyframes fadeIn {
|
||||||
background: white;
|
from { opacity: 0; }
|
||||||
color: black;
|
to { opacity: 1; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
18761
Js/fonctions.js
18761
Js/fonctions.js
File diff suppressed because it is too large
Load Diff
132
Js/sw-register.js
Normal file
132
Js/sw-register.js
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Enregistrement du Service Worker
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
// S'assurer que nous sommes sur HTTPS en production
|
||||||
|
if (location.protocol === 'https:' || location.hostname === 'localhost') {
|
||||||
|
navigator.serviceWorker.register('/service-worker.js')
|
||||||
|
.then(function(registration) {
|
||||||
|
console.log('[Service Worker] Enregistré avec succès:', registration.scope);
|
||||||
|
|
||||||
|
// Vérifier les mises à jour
|
||||||
|
registration.addEventListener('updatefound', () => {
|
||||||
|
const newWorker = registration.installing;
|
||||||
|
console.log('[Service Worker] Mise à jour trouvée:', newWorker.state);
|
||||||
|
|
||||||
|
newWorker.addEventListener('statechange', () => {
|
||||||
|
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
||||||
|
// Nouvelle version disponible
|
||||||
|
console.log('[Service Worker] Nouvelle version disponible');
|
||||||
|
this.showUpdateNotification();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('[Service Worker] Échec de l\'enregistrement:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gestion hors ligne
|
||||||
|
window.addEventListener('online', () => {
|
||||||
|
document.documentElement.classList.remove('offline');
|
||||||
|
console.log('[App] Connexion rétablie');
|
||||||
|
this.showOnlineNotification();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('offline', () => {
|
||||||
|
document.documentElement.classList.add('offline');
|
||||||
|
console.log('[App] Mode hors ligne');
|
||||||
|
this.showOfflineNotification();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification de mise à jour
|
||||||
|
function showUpdateNotification() {
|
||||||
|
if (window.appConfig?.isAnglophone) {
|
||||||
|
if (confirm('A new version is available. Reload the page?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (confirm('Une nouvelle version est disponible. Recharger la page?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification en ligne
|
||||||
|
function showOnlineNotification() {
|
||||||
|
// Créer une notification toast
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.className = 'position-fixed top-0 end-0 p-3';
|
||||||
|
toast.style.zIndex = '9999';
|
||||||
|
|
||||||
|
toast.innerHTML = `
|
||||||
|
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
|
<div class="toast-header bg-success text-white">
|
||||||
|
<i class="bi bi-wifi me-2"></i>
|
||||||
|
<strong class="me-auto">${window.appConfig?.isAnglophone ? 'Online' : 'En ligne'}</strong>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
${window.appConfig?.isAnglophone
|
||||||
|
? 'Connection restored. Synchronization in progress...'
|
||||||
|
: 'Connexion rétablie. Synchronisation en cours...'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
|
||||||
|
// Supprimer après 3 secondes
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.remove();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification hors ligne
|
||||||
|
function showOfflineNotification() {
|
||||||
|
// Créer une notification toast
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.className = 'position-fixed top-0 end-0 p-3';
|
||||||
|
toast.style.zIndex = '9999';
|
||||||
|
|
||||||
|
toast.innerHTML = `
|
||||||
|
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
|
<div class="toast-header bg-warning text-dark">
|
||||||
|
<i class="bi bi-wifi-off me-2"></i>
|
||||||
|
<strong class="me-auto">${window.appConfig?.isAnglophone ? 'Offline' : 'Hors ligne'}</strong>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
${window.appConfig?.isAnglophone
|
||||||
|
? 'No internet connection. Working in offline mode.'
|
||||||
|
: 'Pas de connexion Internet. Mode hors ligne actif.'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
|
||||||
|
// Supprimer après 5 secondes
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.remove();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour forcer la mise à jour du Service Worker
|
||||||
|
function updateServiceWorker() {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.ready.then(registration => {
|
||||||
|
registration.update().then(() => {
|
||||||
|
console.log('[Service Worker] Mise à jour forcée');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exporter pour une utilisation externe
|
||||||
|
window.serviceWorker = {
|
||||||
|
update: updateServiceWorker,
|
||||||
|
isSupported: 'serviceWorker' in navigator
|
||||||
|
};
|
||||||
|
|
@ -40,10 +40,12 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
<!-- Meta pour UX améliorée -->
|
<!-- Meta pour UX améliorée -->
|
||||||
<meta name="description" content="Portail professionnel de gestion santé Inter Santé">
|
<meta name="description" content="Portail professionnel de gestion santé Inter Santé">
|
||||||
<meta name="theme-color" content="#b7472a">
|
<meta name="theme-color" content="#b7472a">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
|
|
||||||
<!-- Progressive Web App -->
|
<!-- Icône -->
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
<link rel="apple-touch-icon" href="Bootstrap_new/images/new/favicon.png">
|
<link rel="apple-touch-icon" href="Bootstrap_new/images/new/favicon.png">
|
||||||
|
<link rel="icon" href="Bootstrap_new/images/new/favicon.png" type="image/png">
|
||||||
|
|
||||||
<!-- Bootstrap 5 -->
|
<!-- Bootstrap 5 -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
@ -64,31 +66,36 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
<!-- SweetAlert2 -->
|
<!-- SweetAlert2 -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Animate.css pour animations douces -->
|
<!-- Animate.css -->
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||||
|
|
||||||
<!-- Office/PowerPoint Inspired Styles -->
|
<!-- Progressive Web App -->
|
||||||
<link href="Bootstrap_new/css/style_office.css?ver=2025.12.20.00" rel="stylesheet">
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<link rel="apple-touch-icon" href="Bootstrap_new/images/new/favicon.png">
|
||||||
|
|
||||||
<!-- UX Improvements CSS -->
|
<!-- jQuery UI (pour Datepicker) -->
|
||||||
<link href="Bootstrap_new/css/ux_enhancements.css?ver=2025.12.20.01" rel="stylesheet">
|
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
|
||||||
|
|
||||||
|
<!-- Styles personnalisés -->
|
||||||
|
<link href="Bootstrap_new/css/style_office.css?ver=2025.12.21.01" rel="stylesheet">
|
||||||
|
<link href="Bootstrap_new/css/ux_enhancements.css?ver=2025.12.21.01" rel="stylesheet">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Mode développeur
|
// Mode développeur
|
||||||
const modeDev = 1; //<?= $_SESSION['modeDev_C'] ?? 0 ?>;
|
const modeDev = <?= $_SESSION['modeDev_C'] ?? 0 ?>;
|
||||||
if (modeDev != "1") {
|
if (modeDev !== 1) {
|
||||||
document.addEventListener('contextmenu', function(e) {
|
document.addEventListener('contextmenu', e => e.preventDefault());
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variables globales accessibles par tous les scripts
|
// Variables globales accessibles par tous les scripts
|
||||||
window.appConfig = {
|
window.appConfig = {
|
||||||
activeParentId: '<?= $activeParentId ?>',
|
activeParentId: '<?= $activeParentId ?>',
|
||||||
|
activeChildId: '<?= $activeChildId ?>',
|
||||||
activeLink: '<?= $activeLink ?>',
|
activeLink: '<?= $activeLink ?>',
|
||||||
racineWeb: '<?= $racineWeb ?>',
|
racineWeb: '<?= $racineWeb ?>',
|
||||||
isAnglophone: <?= est_anglophone() ? 'true' : 'false' ?>,
|
isAnglophone: <?= est_anglophone() ? 'true' : 'false' ?>,
|
||||||
modeTest: <?= $_SESSION['bdTests_C'] == "1" ? 'true' : 'false' ?>
|
modeTest: <?= $_SESSION['bdTests_C'] == "1" ? 'true' : 'false' ?>,
|
||||||
|
userInitials: '<?= $_SESSION['userInitials_C'] ?? 'U' ?>'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -98,11 +105,11 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
<header class="app-header">
|
<header class="app-header">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<div class="logo-container">
|
<div class="logo-container">
|
||||||
<button class="header-btn sidebar-toggle d-lg-none" aria-label="Menu navigation">
|
<button class="header-btn sidebar-toggle d-lg-none" aria-label="Menu navigation" onclick="appUX.toggleSidebar()">
|
||||||
<i class="bi bi-list"></i>
|
<i class="bi bi-list"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<a href="#" class="app-logo">
|
<a href="Accueil" class="app-logo">
|
||||||
<img src="Bootstrap_new/images/new/favicon.png" alt="INTER-SANTÉ" width="36" height="36">
|
<img src="Bootstrap_new/images/new/favicon.png" alt="INTER-SANTÉ" width="36" height="36">
|
||||||
<span class="ms-2">INTER-SANTÉ</span>
|
<span class="ms-2">INTER-SANTÉ</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -166,7 +173,7 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
<i class="fas fa-envelope me-2"></i> <?= _('Messagerie') ?>
|
<i class="fas fa-envelope me-2"></i> <?= _('Messagerie') ?>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item text-danger" href="#">
|
<a class="dropdown-item text-danger" href="Connexion/deconnecter">
|
||||||
<i class="fas fa-sign-out-alt me-2"></i> <?= _('Déconnexion') ?>
|
<i class="fas fa-sign-out-alt me-2"></i> <?= _('Déconnexion') ?>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -190,20 +197,24 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Déterminer si le menu doit être ouvert
|
||||||
|
$shouldBeOpen = $isParentActive || $hasActiveChild;
|
||||||
?>
|
?>
|
||||||
<div class="nav-item">
|
<div class="nav-item">
|
||||||
<?php if (sizeof($menuChildrenLevelOne) > 0): ?>
|
<?php if (sizeof($menuChildrenLevelOne) > 0): ?>
|
||||||
<a href="#submenu<?= $key0 ?>"
|
<a href="#submenu<?= $key0 ?>"
|
||||||
class="nav-link <?= $isParentActive || $hasActiveChild ? 'active' : '' ?>"
|
class="nav-link <?= $shouldBeOpen ? 'active' : '' ?>"
|
||||||
data-bs-toggle="collapse"
|
data-bs-toggle="collapse"
|
||||||
aria-expanded="<?= $isParentActive || $hasActiveChild ? 'true' : 'false' ?>"
|
onclick="appNavigation.toggleMenu('submenu<?= $key0 ?>', event)"
|
||||||
|
aria-expanded="<?= $shouldBeOpen ? 'true' : 'false' ?>"
|
||||||
aria-controls="submenu<?= $key0 ?>">
|
aria-controls="submenu<?= $key0 ?>">
|
||||||
<i class="<?= $menuParent['icone'] ?>"></i>
|
<i class="<?= $menuParent['icone'] ?>"></i>
|
||||||
<span class="nav-text"><?= $menuParent['libeleMenu'] ?></span>
|
<span class="nav-text"><?= $menuParent['libeleMenu'] ?></span>
|
||||||
<i class="nav-arrow bi bi-chevron-right"></i>
|
<i class="nav-arrow bi bi-chevron-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="nav-submenu collapse <?= $isParentActive || $hasActiveChild ? 'show' : '' ?>"
|
<div class="nav-submenu collapse <?= $shouldBeOpen ? 'show' : '' ?>"
|
||||||
id="submenu<?= $key0 ?>">
|
id="submenu<?= $key0 ?>">
|
||||||
<?php foreach ($menuChildrenLevelOne as $key1 => $menuChild):
|
<?php foreach ($menuChildrenLevelOne as $key1 => $menuChild):
|
||||||
$childActive = (explode('/', $menuChild['lienMenu'])[0] ?? '') == $activeLink;
|
$childActive = (explode('/', $menuChild['lienMenu'])[0] ?? '') == $activeLink;
|
||||||
|
|
@ -258,6 +269,8 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
<input type="hidden" id="dureeSession" value="<?= $_SESSION['dureeSession'] ?>">
|
<input type="hidden" id="dureeSession" value="<?= $_SESSION['dureeSession'] ?>">
|
||||||
<input type="hidden" id="nomSociete" value="<?= $_SESSION['nomSociete'] ?>">
|
<input type="hidden" id="nomSociete" value="<?= $_SESSION['nomSociete'] ?>">
|
||||||
<input type="hidden" id="nomClient" value="<?= htmlspecialchars($_SESSION['nomClient_C']) ?>">
|
<input type="hidden" id="nomClient" value="<?= htmlspecialchars($_SESSION['nomClient_C']) ?>">
|
||||||
|
<input type="hidden" id="idBeneficiaire_C" value="<?= $_SESSION['idBeneficiaire_C'] ?>">
|
||||||
|
<input type="hidden" id="okId" value="<?= $_SESSION['okId'] ?>">
|
||||||
|
|
||||||
<!-- Content Container -->
|
<!-- Content Container -->
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
|
|
@ -421,14 +434,16 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- JavaScript Libraries -->
|
<!-- JavaScript Libraries -->
|
||||||
<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 src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
||||||
|
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="https://cdn.datatables.net/v/bs5/dt-1.13.6/datatables.min.js"></script>
|
<script src="https://cdn.datatables.net/v/bs5/dt-1.13.6/datatables.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
|
||||||
<!-- Application Scripts -->
|
<!-- Application Scripts -->
|
||||||
<script src="Js/fonctions.js?ver=2025.12.20.00"></script>
|
<script src="Js/fonctions.js?ver=2025.12.21.01"></script>
|
||||||
|
|
||||||
<?php if (est_anglophone()): ?>
|
<?php if (est_anglophone()): ?>
|
||||||
<script src="Js/datepicker-eng.js"></script>
|
<script src="Js/datepicker-eng.js"></script>
|
||||||
|
|
@ -437,9 +452,9 @@ foreach ($menus as $key0 => $menuParent) {
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- UX Enhancement Script -->
|
<!-- UX Enhancement Script -->
|
||||||
<script src="Bootstrap_new/js/ux-manager.js?ver=2025.12.20.01"></script>
|
<script src="Bootstrap_new/js/ux-manager.js?ver=2025.12.21.01"></script>
|
||||||
|
|
||||||
<!-- AJAX Content Area -->
|
<!-- Service Worker Registration -->
|
||||||
<div id="div_ajaxgabarit"></div>
|
<script src="Js/sw-register.js?ver=2025.12.21.01"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
56
manifest.json
Normal file
56
manifest.json
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"name": "Portail RH Inter Santé",
|
||||||
|
"short_name": "InterSanté RH",
|
||||||
|
"description": "Portail RH professionnel avec design Office/PowerPoint",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#f3f2f1",
|
||||||
|
"theme_color": "#b7472a",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-128x128.png",
|
||||||
|
"sizes": "128x128",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-152x152.png",
|
||||||
|
"sizes": "152x152",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"categories": ["business", "productivity", "utilities"],
|
||||||
|
"dir": "ltr",
|
||||||
|
"lang": "fr-FR",
|
||||||
|
"prefer_related_applications": false
|
||||||
|
}
|
||||||
124
service-worker.js
Normal file
124
service-worker.js
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Service Worker pour Portail RH Inter Santé
|
||||||
|
const CACHE_NAME = 'inter-sante-portal-v1.0';
|
||||||
|
const OFFLINE_URL = '/offline.html';
|
||||||
|
|
||||||
|
// Ressources à mettre en cache immédiatement
|
||||||
|
const PRECACHE_URLS = [
|
||||||
|
'/',
|
||||||
|
'/style_office.css',
|
||||||
|
'/ux_enhancements.css',
|
||||||
|
'/ux-manager.js',
|
||||||
|
'/manifest.json',
|
||||||
|
'/icons/icon-192x192.png',
|
||||||
|
'/icons/icon-512x512.png'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Installation - Pré-cache des ressources essentielles
|
||||||
|
self.addEventListener('install', event => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then(cache => {
|
||||||
|
console.log('[Service Worker] Pré-cache des ressources');
|
||||||
|
return cache.addAll(PRECACHE_URLS);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log('[Service Worker] Installation terminée');
|
||||||
|
return self.skipWaiting();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Activation - Nettoyage des anciens caches
|
||||||
|
self.addEventListener('activate', event => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys().then(cacheNames => {
|
||||||
|
return Promise.all(
|
||||||
|
cacheNames.map(cacheName => {
|
||||||
|
if (cacheName !== CACHE_NAME) {
|
||||||
|
console.log('[Service Worker] Suppression ancien cache:', cacheName);
|
||||||
|
return caches.delete(cacheName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}).then(() => {
|
||||||
|
console.log('[Service Worker] Activation terminée');
|
||||||
|
return self.clients.claim();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stratégie de cache: Network First, fallback Cache
|
||||||
|
self.addEventListener('fetch', event => {
|
||||||
|
// Ignorer les requêtes non-GET et les requêtes cross-origin
|
||||||
|
if (event.request.method !== 'GET' ||
|
||||||
|
!event.request.url.startsWith(self.location.origin)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour les pages HTML: Network First
|
||||||
|
if (event.request.headers.get('accept').includes('text/html')) {
|
||||||
|
event.respondWith(
|
||||||
|
fetch(event.request)
|
||||||
|
.then(response => {
|
||||||
|
// Mettre à jour le cache avec la nouvelle version
|
||||||
|
const responseClone = response.clone();
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then(cache => cache.put(event.request, responseClone));
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Fallback au cache ou page offline
|
||||||
|
return caches.match(event.request)
|
||||||
|
.then(cachedResponse => {
|
||||||
|
return cachedResponse || caches.match(OFFLINE_URL);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour les autres ressources: Cache First
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(event.request)
|
||||||
|
.then(cachedResponse => {
|
||||||
|
if (cachedResponse) {
|
||||||
|
return cachedResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(event.request)
|
||||||
|
.then(response => {
|
||||||
|
// Ne pas mettre en cache les erreurs
|
||||||
|
if (!response || response.status !== 200 || response.type !== 'basic') {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre en cache pour la prochaine fois
|
||||||
|
const responseToCache = response.clone();
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then(cache => cache.put(event.request, responseToCache));
|
||||||
|
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Pour les CSS/JS, on peut retourner une réponse vide
|
||||||
|
if (event.request.url.includes('.css')) {
|
||||||
|
return new Response('/* Ressource non disponible hors ligne */', {
|
||||||
|
headers: { 'Content-Type': 'text/css' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (event.request.url.includes('.js')) {
|
||||||
|
return new Response('// Ressource non disponible hors ligne', {
|
||||||
|
headers: { 'Content-Type': 'application/javascript' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gestion des messages
|
||||||
|
self.addEventListener('message', event => {
|
||||||
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||||
|
self.skipWaiting();
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user