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 {
|
||||
/* PowerPoint/Office 2019 Color Palette */
|
||||
--office-primary: #b7472a; /* Sidebar color - PowerPoint accent */
|
||||
--office-secondary: #2b579a; /* Header/buttons - Office blue */
|
||||
--office-accent: #107c10; /* Success/active states - Excel green */
|
||||
--office-light: #f3f2f1; /* Background - Office gray */
|
||||
--office-common: #e6e6e6; /* Background - Office gray */
|
||||
--office-dark: #323130; /* Text color */
|
||||
--office-border: #d0d0d0; /* Borders */
|
||||
--office-hover: #f0f0f0; /* Hover states */
|
||||
--office-card: #ffffff; /* Card backgrounds */
|
||||
|
||||
/* Dimensions */
|
||||
--sidebar-width: 260px;
|
||||
--sidebar-collapsed: 70px;
|
||||
--header-height: 64px;
|
||||
--transition-speed: 0.3s;
|
||||
|
||||
/* Responsive breakpoints */
|
||||
--breakpoint-tablet: 1200px;
|
||||
--breakpoint-mobile: 768px;
|
||||
/* Animation pour les sous-menus */
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
max-height: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
max-height: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
BASE STYLES & RESET
|
||||
============================================ */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
max-height: 500px;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
|
||||
background-color: var(--office-common);
|
||||
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;
|
||||
/* Styles pour le menu unique ouvert */
|
||||
.nav-submenu {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
animation: slideUp 0.3s ease forwards;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
.nav-submenu.show {
|
||||
display: block;
|
||||
animation: slideDown 0.3s ease forwards;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
/* Désactiver la logique Bootstrap par défaut */
|
||||
.nav-link[data-bs-toggle="collapse"] {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.nav-link i {
|
||||
width: 24px;
|
||||
font-size: 18px;
|
||||
margin-right: 12px;
|
||||
text-align: center;
|
||||
.nav-link[data-bs-toggle="collapse"].collapsed .nav-arrow {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
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 {
|
||||
.nav-link[data-bs-toggle="collapse"]:not(.collapsed) .nav-arrow {
|
||||
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 {
|
||||
padding: 24px;
|
||||
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 {
|
||||
/* Zone de détection de proximité */
|
||||
.proximity-hover-area {
|
||||
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;
|
||||
top: 50%;
|
||||
width: 80px;
|
||||
height: 200px;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1005;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.context-header {
|
||||
background: linear-gradient(135deg, var(--office-primary), #ee6a49);
|
||||
/* État hors ligne */
|
||||
body.offline .app-header::after {
|
||||
content: 'Hors ligne';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #ff6b35;
|
||||
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;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-left: 12px;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
LANGUAGE SELECTOR
|
||||
============================================ */
|
||||
.language-selector {
|
||||
/* Spinner amélioré */
|
||||
.spinner-responsive {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 12px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
color: #313c4c;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.language-selector:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
.spinner-responsive i {
|
||||
font-size: 48px;
|
||||
color: var(--office-primary);
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.language-flag {
|
||||
width: 24px;
|
||||
height: 16px;
|
||||
object-fit: cover;
|
||||
border-radius: 2px;
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
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) {
|
||||
.app-sidebar {
|
||||
transform: translateX(-100%);
|
||||
width: var(--sidebar-width);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.app-sidebar.open {
|
||||
.app-sidebar.show {
|
||||
transform: translateX(0);
|
||||
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.app-main {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-text,
|
||||
.nav-title,
|
||||
.nav-badge,
|
||||
.nav-arrow {
|
||||
display: block;
|
||||
.context-toggle {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
SCROLLBAR STYLING
|
||||
ACCESSIBILITÉ
|
||||
============================================ */
|
||||
.app-sidebar::-webkit-scrollbar,
|
||||
.context-body::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
|
||||
.nav-link:focus {
|
||||
outline: 2px solid var(--office-secondary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.app-sidebar::-webkit-scrollbar-track,
|
||||
.context-body::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 3px;
|
||||
.context-toggle:focus {
|
||||
outline: 3px solid var(--office-secondary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.app-sidebar::-webkit-scrollbar-thumb,
|
||||
.context-body::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
/* High contrast mode */
|
||||
@media (prefers-contrast: high) {
|
||||
:root {
|
||||
--office-border: #000000;
|
||||
--office-dark: #000000;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
outline: 3px solid #000;
|
||||
}
|
||||
}
|
||||
|
||||
.app-sidebar::-webkit-scrollbar-thumb:hover,
|
||||
.context-body::-webkit-scrollbar-thumb:hover {
|
||||
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 {
|
||||
/* Variables UX avancées */
|
||||
--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 {
|
||||
/* Effet de vague au clic */
|
||||
.ripple {
|
||||
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;
|
||||
}
|
||||
|
||||
.nav-submenu.show {
|
||||
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 {
|
||||
.ripple::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border: 2px solid rgba(183, 71, 42, 0.3);
|
||||
border-radius: 50%;
|
||||
animation: ripple 3s infinite;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
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 {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
opacity: 1;
|
||||
0% {
|
||||
transform: scale(0, 0);
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* État au survol et activation */
|
||||
.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);
|
||||
20% {
|
||||
transform: scale(25, 25);
|
||||
opacity: 0.3;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale(40, 40);
|
||||
}
|
||||
}
|
||||
|
||||
/* Effet de levitation pour les cartes */
|
||||
.content-card {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fcfcfc 100%);
|
||||
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;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.content-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: var(--ux-shadow-medium);
|
||||
border-color: rgba(43, 87, 154, 0.1);
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
LOADING STATES
|
||||
============================================ */
|
||||
.loading-placeholder {
|
||||
background: linear-gradient(90deg,
|
||||
#f0f0f0 25%,
|
||||
#e0e0e0 50%,
|
||||
#f0f0f0 75%);
|
||||
/* Animation du bouton contexte */
|
||||
.context-toggle {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.context-toggle:hover {
|
||||
transform: scale(1.1) translateY(-2px);
|
||||
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%;
|
||||
animation: loading 1.5s infinite;
|
||||
border-radius: var(--ux-border-radius-sm);
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
|
|
@ -560,274 +73,66 @@ body:has(.proximity-hover-area:hover) .context-toggle {
|
|||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
ACCESSIBILITY ENHANCEMENTS
|
||||
============================================ */
|
||||
|
||||
/* 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);
|
||||
/* Smooth scrolling */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Lien skip pour accessibilité */
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 0;
|
||||
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;
|
||||
/* Focus styles améliorés */
|
||||
:focus-visible {
|
||||
outline: 3px solid var(--office-secondary);
|
||||
outline-offset: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
outline: 3px solid white;
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* Animation d'entrée */
|
||||
.fade-in-up {
|
||||
animation: fadeInUp 0.6s ease forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
@media print {
|
||||
.app-sidebar,
|
||||
.app-header,
|
||||
.context-panel,
|
||||
.context-toggle,
|
||||
.nav-bar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.app-main {
|
||||
margin-left: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
/* Tooltip personnalisé */
|
||||
[data-tooltip] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[data-tooltip]:hover::before {
|
||||
content: attr(data-tooltip);
|
||||
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;
|
||||
}
|
||||
|
||||
[data-tooltip]:hover::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 6px solid transparent;
|
||||
border-top-color: var(--office-dark);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
18765
Js/fonctions.js
18765
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
|
||||
};
|
||||
|
|
@ -34,61 +34,68 @@ foreach ($menus as $key0 => $menuParent) {
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<base href="<?= $racineWeb ?>">
|
||||
|
||||
|
||||
<title><?= htmlspecialchars($_SESSION['vue'] ?? 'INTER SANTÉ') ?> | Portail Santé</title>
|
||||
|
||||
|
||||
<!-- Meta pour UX améliorée -->
|
||||
<meta name="description" content="Portail professionnel de gestion santé Inter Santé">
|
||||
<meta name="theme-color" content="#b7472a">
|
||||
|
||||
<!-- Progressive Web App -->
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
<!-- Icône -->
|
||||
<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 -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
||||
|
||||
<!-- Select2 -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/@ttskch/select2-bootstrap5-theme@1.3.0/dist/select2-bootstrap5.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- DataTables -->
|
||||
<link href="https://cdn.datatables.net/v/bs5/dt-1.13.6/datatables.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- SweetAlert2 -->
|
||||
<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">
|
||||
|
||||
<!-- Office/PowerPoint Inspired Styles -->
|
||||
<link href="Bootstrap_new/css/style_office.css?ver=2025.12.20.00" rel="stylesheet">
|
||||
|
||||
<!-- UX Improvements CSS -->
|
||||
<link href="Bootstrap_new/css/ux_enhancements.css?ver=2025.12.20.01" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- Progressive Web App -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="apple-touch-icon" href="Bootstrap_new/images/new/favicon.png">
|
||||
|
||||
<!-- jQuery UI (pour Datepicker) -->
|
||||
<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>
|
||||
// Mode développeur
|
||||
const modeDev = 1; //<?= $_SESSION['modeDev_C'] ?? 0 ?>;
|
||||
if (modeDev != "1") {
|
||||
document.addEventListener('contextmenu', function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
const modeDev = <?= $_SESSION['modeDev_C'] ?? 0 ?>;
|
||||
if (modeDev !== 1) {
|
||||
document.addEventListener('contextmenu', e => e.preventDefault());
|
||||
}
|
||||
|
||||
|
||||
// Variables globales accessibles par tous les scripts
|
||||
window.appConfig = {
|
||||
activeParentId: '<?= $activeParentId ?>',
|
||||
activeChildId: '<?= $activeChildId ?>',
|
||||
activeLink: '<?= $activeLink ?>',
|
||||
racineWeb: '<?= $racineWeb ?>',
|
||||
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>
|
||||
</head>
|
||||
|
|
@ -98,11 +105,11 @@ foreach ($menus as $key0 => $menuParent) {
|
|||
<header class="app-header">
|
||||
<div class="header-content">
|
||||
<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>
|
||||
</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">
|
||||
<span class="ms-2">INTER-SANTÉ</span>
|
||||
</a>
|
||||
|
|
@ -166,7 +173,7 @@ foreach ($menus as $key0 => $menuParent) {
|
|||
<i class="fas fa-envelope me-2"></i> <?= _('Messagerie') ?>
|
||||
</a>
|
||||
<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') ?>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -190,20 +197,24 @@ foreach ($menus as $key0 => $menuParent) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Déterminer si le menu doit être ouvert
|
||||
$shouldBeOpen = $isParentActive || $hasActiveChild;
|
||||
?>
|
||||
<div class="nav-item">
|
||||
<?php if (sizeof($menuChildrenLevelOne) > 0): ?>
|
||||
<a href="#submenu<?= $key0 ?>"
|
||||
class="nav-link <?= $isParentActive || $hasActiveChild ? 'active' : '' ?>"
|
||||
class="nav-link <?= $shouldBeOpen ? 'active' : '' ?>"
|
||||
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 ?>">
|
||||
<i class="<?= $menuParent['icone'] ?>"></i>
|
||||
<span class="nav-text"><?= $menuParent['libeleMenu'] ?></span>
|
||||
<i class="nav-arrow bi bi-chevron-right"></i>
|
||||
</a>
|
||||
|
||||
<div class="nav-submenu collapse <?= $isParentActive || $hasActiveChild ? 'show' : '' ?>"
|
||||
<div class="nav-submenu collapse <?= $shouldBeOpen ? 'show' : '' ?>"
|
||||
id="submenu<?= $key0 ?>">
|
||||
<?php foreach ($menuChildrenLevelOne as $key1 => $menuChild):
|
||||
$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="nomSociete" value="<?= $_SESSION['nomSociete'] ?>">
|
||||
<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 -->
|
||||
<div class="content-card">
|
||||
|
|
@ -421,25 +434,27 @@ foreach ($menus as $key0 => $menuParent) {
|
|||
</div>
|
||||
|
||||
<!-- 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/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.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>
|
||||
|
||||
|
||||
<!-- 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()): ?>
|
||||
<script src="Js/datepicker-eng.js"></script>
|
||||
<?php else: ?>
|
||||
<script src="Js/datepicker-fr.js"></script>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<!-- 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 -->
|
||||
<div id="div_ajaxgabarit"></div>
|
||||
<!-- Service Worker Registration -->
|
||||
<script src="Js/sw-register.js?ver=2025.12.21.01"></script>
|
||||
</body>
|
||||
</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