Page admin : statistiques réparées et refonte responsive dans la DA du site
Build & Deploy / build (push) Successful in 22s
Build & Deploy / build (push) Successful in 22s
Statistiques : le type Stats attendait cir/sccs/users alors que /admin/stats renvoie cir_documents/sccs_documents/total_users. Trois cartes sur quatre affichaient « - » et les deux barres restaient à zéro. Contrat aligné sur l'API. Style : la feuille n'utilisait aucune couleur du site — palette bleu ardoise, unités en px, cartes sans bordure. Reprise de la DA des autres pages : accent violet #7c3aed (celui de la Sidebar), échelle de gris et rayons 0.5/0.75rem de DocumentsPage, cartes blanches bordées, unités rem. Responsive : - onglets en bande défilante au lieu d'un retour à la ligne qui cassait l'entête ; - les tableaux deviennent des cartes sous 768px, chaque cellule portant son intitulé de colonne via data-label + ::before — l'ancien display:block empilait les valeurs sans dire à quoi elles correspondaient ; - cartes de stats en grille auto-fit, plus de paliers codés en dur ; - panneau scrapers en deux colonnes, empilé sous 1024px, boutons pleine largeur sous 480px ; - modale contrainte à la largeur de l'écran, focus visible au clavier. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+578
-54
@@ -1,58 +1,582 @@
|
||||
.page{padding:20px;max-width:1200px;margin:0 auto}
|
||||
.header{display:flex;align-items:center;justify-content:space-between;gap:16px}
|
||||
.tabs{display:flex;gap:8px}
|
||||
.tabs button{background:transparent;border:1px solid #e6e9ef;padding:8px 12px;border-radius:8px;cursor:pointer}
|
||||
.tabs .active{background:#eef2ff;border-color:#c7b3ff}
|
||||
.content{margin-top:16px}
|
||||
.spinner{padding:12px;background:#f8fafc;border:1px solid #e6eef6;border-radius:8px}
|
||||
.error{padding:12px;background:#fff1f2;border:1px solid #fecaca;color:#b91c1c;border-radius:8px}
|
||||
/* Administration — aligné sur la DA des autres pages :
|
||||
accent violet #7c3aed (celui de la Sidebar), échelle de gris Tailwind,
|
||||
cartes blanches bordées, rayons 0.5/0.75rem, unités rem. */
|
||||
|
||||
.panelHeader{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;gap:12px}
|
||||
.search{padding:8px;border:1px solid #e6eef6;border-radius:6px}
|
||||
|
||||
.tableWrap{overflow:auto;background:#fff;border:1px solid #eef2f7;padding:8px;border-radius:8px}
|
||||
.table{width:100%;border-collapse:collapse}
|
||||
.table th,.table td{padding:10px;border-bottom:1px solid #f1f5f9;text-align:left}
|
||||
.rowActions{display:flex;gap:8px}
|
||||
.danger{background:#ef4444;color:#fff;border:none;padding:8px 10px;border-radius:6px;cursor:pointer}
|
||||
|
||||
.badgeAdmin{background:#7c3aed;color:#fff;padding:4px 8px;border-radius:999px;font-weight:600}
|
||||
.badgeUser{background:#94a3b8;color:#fff;padding:4px 8px;border-radius:999px;font-weight:600}
|
||||
.badgeCIR{background:#2563eb;color:#fff;padding:4px 8px;border-radius:6px}
|
||||
.badgeSCCS{background:#10b981;color:#fff;padding:4px 8px;border-radius:6px}
|
||||
|
||||
.empty{padding:12px;color:#64748b}
|
||||
|
||||
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}
|
||||
.card{background:#fff;padding:16px;border-radius:8px;display:flex;flex-direction:column;gap:8px;align-items:flex-start}
|
||||
.cardLabel{font-size:14px;color:#64748b}
|
||||
.cardValue{font-size:20px;font-weight:700}
|
||||
|
||||
.chartWrap{background:#fff;padding:12px;border-radius:8px}
|
||||
.bars{display:flex;flex-direction:column;gap:8px}
|
||||
.barRow{display:flex;align-items:center;gap:8px}
|
||||
.barTitle{width:60px}
|
||||
.barFill{height:14px;background:#2563eb;border-radius:6px;flex:1}
|
||||
.barFillAlt{background:#10b981}
|
||||
.barVal{width:40px;text-align:right}
|
||||
|
||||
.scraperControls{display:flex;gap:20px;align-items:flex-start}
|
||||
.logList{background:#fff;padding:12px;border-radius:8px}
|
||||
.status{font-weight:700}
|
||||
|
||||
.modalBackdrop{position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(2,6,23,0.45);display:flex;align-items:center;justify-content:center;z-index:9999}
|
||||
.modal{background:#fff;padding:20px;border-radius:8px;min-width:320px}
|
||||
.modalActions{display:flex;gap:8px;justify-content:flex-end;margin-top:12px}
|
||||
|
||||
@media (max-width: 768px){
|
||||
.cards{grid-template-columns:repeat(2,1fr)}
|
||||
.table th, .table td{display:block}
|
||||
.table tr{margin-bottom:12px;border-bottom:none}
|
||||
.tableWrap{padding:0}
|
||||
.page {
|
||||
padding: 2rem;
|
||||
max-width: 80rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (max-width: 480px){
|
||||
.cards{grid-template-columns:1fr}
|
||||
.scraperControls{flex-direction:column}
|
||||
.tabs{flex-wrap:wrap}
|
||||
/* ── En-tête et onglets ─────────────────────────────────────── */
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1.75rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
color: rgb(17, 24, 39);
|
||||
line-height: 1.1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.tabs button {
|
||||
background: transparent;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
color: rgb(55, 65, 81);
|
||||
padding: 0.5rem 0.875rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.tabs button:hover {
|
||||
background: rgb(249, 250, 251);
|
||||
border-color: rgb(209, 213, 219);
|
||||
}
|
||||
|
||||
.tabs .active,
|
||||
.tabs .active:hover {
|
||||
background: rgba(124, 58, 237, 0.08);
|
||||
border-color: rgba(124, 58, 237, 0.45);
|
||||
color: #7c3aed;
|
||||
}
|
||||
|
||||
.tabs button:focus-visible,
|
||||
.danger:focus-visible,
|
||||
.search:focus-visible {
|
||||
outline: 2px solid #7c3aed;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* ── États ──────────────────────────────────────────────────── */
|
||||
|
||||
.spinner {
|
||||
padding: 0.875rem 1rem;
|
||||
background: rgb(249, 250, 251);
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
border-radius: 0.75rem;
|
||||
color: rgb(75, 85, 99);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
padding: 0.875rem 1rem;
|
||||
background: rgb(254, 242, 242);
|
||||
border: 1px solid rgb(254, 202, 202);
|
||||
color: rgb(185, 28, 28);
|
||||
border-radius: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
color: rgb(107, 114, 128);
|
||||
}
|
||||
|
||||
/* ── Barre de panneau ───────────────────────────────────────── */
|
||||
|
||||
.panelHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1rem;
|
||||
color: rgb(75, 85, 99);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.search {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9375rem;
|
||||
min-width: 14rem;
|
||||
flex: 1 1 14rem;
|
||||
max-width: 22rem;
|
||||
}
|
||||
|
||||
/* ── Tableaux ───────────────────────────────────────────────── */
|
||||
|
||||
.tableWrap {
|
||||
background: white;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
border-radius: 0.75rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 0.75rem 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgb(243, 244, 246);
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: rgb(249, 250, 251);
|
||||
color: rgb(55, 65, 81);
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.table th:hover {
|
||||
color: #7c3aed;
|
||||
}
|
||||
|
||||
.table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: rgb(249, 250, 251);
|
||||
}
|
||||
|
||||
.rowActions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.rowActions button {
|
||||
background: white;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
color: rgb(55, 65, 81);
|
||||
padding: 0.375rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rowActions button:hover {
|
||||
border-color: rgba(124, 58, 237, 0.45);
|
||||
color: #7c3aed;
|
||||
}
|
||||
|
||||
.danger,
|
||||
.rowActions .danger {
|
||||
background: rgb(239, 68, 68);
|
||||
color: white;
|
||||
border: 1px solid rgb(239, 68, 68);
|
||||
}
|
||||
|
||||
.danger:hover,
|
||||
.rowActions .danger:hover {
|
||||
background: rgb(220, 38, 38);
|
||||
border-color: rgb(220, 38, 38);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ── Badges ─────────────────────────────────────────────────── */
|
||||
|
||||
.badgeAdmin,
|
||||
.badgeUser,
|
||||
.badgeCIR,
|
||||
.badgeSCCS {
|
||||
display: inline-block;
|
||||
padding: 0.1875rem 0.625rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.badgeAdmin,
|
||||
.badgeUser {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.badgeAdmin {
|
||||
background: rgba(124, 58, 237, 0.1);
|
||||
color: #6d28d9;
|
||||
border: 1px solid rgba(124, 58, 237, 0.35);
|
||||
}
|
||||
|
||||
.badgeUser {
|
||||
background: rgb(243, 244, 246);
|
||||
color: rgb(75, 85, 99);
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
}
|
||||
|
||||
.badgeCIR,
|
||||
.badgeSCCS {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.badgeCIR {
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: rgb(29, 78, 216);
|
||||
border: 1px solid rgba(37, 99, 235, 0.3);
|
||||
}
|
||||
|
||||
.badgeSCCS {
|
||||
background: rgba(16, 185, 129, 0.12);
|
||||
color: rgb(4, 120, 87);
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
/* ── Statistiques ───────────────────────────────────────────── */
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.card svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
color: #7c3aed;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.cardLabel {
|
||||
font-size: 0.875rem;
|
||||
color: rgb(107, 114, 128);
|
||||
}
|
||||
|
||||
.cardValue {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: rgb(17, 24, 39);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.chartWrap {
|
||||
background: white;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.chartLabel {
|
||||
font-weight: 600;
|
||||
color: rgb(17, 24, 39);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.bars {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.barRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.barTitle {
|
||||
width: 3.5rem;
|
||||
flex-shrink: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: rgb(55, 65, 81);
|
||||
}
|
||||
|
||||
/* La largeur est posée en style inline par le composant : min-width garde une
|
||||
amorce visible quand la valeur est nulle. */
|
||||
.barFill {
|
||||
height: 0.625rem;
|
||||
min-width: 0.625rem;
|
||||
background: rgb(37, 99, 235);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.barFillAlt {
|
||||
background: rgb(16, 185, 129);
|
||||
}
|
||||
|
||||
.barVal {
|
||||
width: 3rem;
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: rgb(75, 85, 99);
|
||||
}
|
||||
|
||||
/* ── Scrapers ───────────────────────────────────────────────── */
|
||||
|
||||
.scraperControls {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.scraperControls > div:first-child {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.scraperControls button {
|
||||
background: #7c3aed;
|
||||
color: white;
|
||||
border: 1px solid #7c3aed;
|
||||
padding: 0.625rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.scraperControls button:hover {
|
||||
background: #6d28d9;
|
||||
border-color: #6d28d9;
|
||||
}
|
||||
|
||||
.logList {
|
||||
background: white;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.logList h4 {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 600;
|
||||
color: rgb(17, 24, 39);
|
||||
}
|
||||
|
||||
.logList ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.logList li {
|
||||
padding: 0.625rem 0.75rem;
|
||||
background: rgb(249, 250, 251);
|
||||
border: 1px solid rgb(243, 244, 246);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
color: rgb(75, 85, 99);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-weight: 600;
|
||||
color: rgb(17, 24, 39);
|
||||
}
|
||||
|
||||
/* ── Modale ─────────────────────────────────────────────────── */
|
||||
|
||||
.modalBackdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(17, 24, 39, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: white;
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
width: 100%;
|
||||
max-width: 26rem;
|
||||
box-shadow: 0 10px 25px rgba(17, 24, 39, 0.15);
|
||||
}
|
||||
|
||||
.modalActions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
margin-top: 1.25rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.modalActions button {
|
||||
background: white;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
color: rgb(55, 65, 81);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9375rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modalActions button:last-child {
|
||||
background: #7c3aed;
|
||||
border-color: #7c3aed;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ── Responsive ─────────────────────────────────────────────── */
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.scraperControls {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.scraperControls > div:first-child {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page {
|
||||
padding: 1.25rem 1rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 0.875rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
/* Onglets en bande défilante plutôt qu'en retour à la ligne. */
|
||||
.tabs {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.25rem;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.tabs::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Chaque ligne devient une carte, avec l'intitulé de colonne en regard. */
|
||||
.tableWrap {
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.table,
|
||||
.table tbody,
|
||||
.table tr,
|
||||
.table td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table tr {
|
||||
background: white;
|
||||
border: 1px solid rgb(229, 231, 235);
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.25rem 0;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.table td {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-bottom: none;
|
||||
text-align: right;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.table td::before {
|
||||
content: attr(data-label);
|
||||
font-weight: 600;
|
||||
color: rgb(107, 114, 128);
|
||||
font-size: 0.8125rem;
|
||||
text-align: left;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.rowActions {
|
||||
justify-content: stretch;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid rgb(243, 244, 246);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.rowActions button {
|
||||
flex: 1;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.search {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.scraperControls > div:first-child {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scraperControls button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.barTitle {
|
||||
width: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* NB : le site n'a pas de mode sombre réel — index.css bascule --bg sur le body
|
||||
mais aucune page n'utilise ces variables et toutes gardent des cartes claires.
|
||||
Cette page suit la même convention pour rester cohérente avec ses sœurs. */
|
||||
|
||||
Reference in New Issue
Block a user