Build & Deploy / build (push) Successful in 24s
Mesuré au navigateur avant/après (scrollWidth vs clientWidth, 320/375/414/768/
900/1024/1280px) : la page débordait partout, de 17px en mode carte et de 32px
au-dessus de 1024. Deux causes, aucune liée aux points de rupture.
1. L'application tourne en content-box. src/styles/global.css porte bien un
`* { box-sizing: border-box }` mais n'est importé nulle part — main.tsx ne
charge que index.css, qui pose border-box sur le seul #root. Tout width:100%
assorti d'un padding débordait donc de son conteneur. Réinitialisation
cantonnée à .page : global.css est un vestige d'une ancienne maquette (son
propre .sidebar, .layout, un autre fond de body), l'importer déplacerait tout
le site.
2. En mode carte, .table td était en display:flex : le texte de la cellule
devenait un élément flex anonyme, avec min-width:auto, et un titre de
document long refusait de se réduire — il sortait de la carte et entraînait
la page entière, onglets compris. Passé en bloc, intitulé au-dessus de la
valeur, avec coupure des mots.
Aussi : min-width:0 sur .tabs, sans quoi son overflow-x:auto ne s'enclenchait
jamais ; et rétablissement de la rangée d'actions, dont le display:flex était
écrasé en spécificité par `.table td { display: block }`.
Après correction : aucun débordement sur les quatre onglets, de 320 à 1280px.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
625 lines
12 KiB
CSS
625 lines
12 KiB
CSS
/* 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. */
|
|
|
|
/* main.tsx ne charge pas styles/global.css — vestige d'une ancienne maquette,
|
|
jamais importé — donc son `* { box-sizing: border-box }` ne s'applique pas et
|
|
l'application tourne en content-box : tout width:100% assorti d'un padding
|
|
déborde de son conteneur. Réinitialisation cantonnée à cette page pour ne rien
|
|
déplacer sur le reste du site. */
|
|
.page,
|
|
.page *,
|
|
.page *::before,
|
|
.page *::after {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.page {
|
|
padding: 2rem;
|
|
width: 100%;
|
|
max-width: 80rem;
|
|
min-width: 0;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* ── En-tête et onglets ─────────────────────────────────────── */
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
min-width: 0;
|
|
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;
|
|
min-width: 0;
|
|
max-width: 100%;
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
padding-bottom: 0.125rem;
|
|
}
|
|
|
|
.tabs::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.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;
|
|
flex-shrink: 0;
|
|
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;
|
|
max-width: 100%;
|
|
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);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
/* Sans cette borne, un seul titre long étire la colonne et donc tout le tableau. */
|
|
.table td:nth-child(2) {
|
|
min-width: 12rem;
|
|
max-width: 26rem;
|
|
}
|
|
|
|
.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: 900px) {
|
|
.page {
|
|
padding: 1.25rem 1rem;
|
|
}
|
|
|
|
.header {
|
|
align-items: flex-start;
|
|
flex-direction: column;
|
|
gap: 0.875rem;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 1.75rem;
|
|
}
|
|
|
|
.tabs {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Chaque ligne devient une carte, avec l'intitulé de colonne en regard. */
|
|
.tableWrap {
|
|
background: transparent;
|
|
border: none;
|
|
border-radius: 0;
|
|
max-width: 100%;
|
|
overflow-x: visible;
|
|
}
|
|
|
|
.table,
|
|
.table tbody,
|
|
.table tr,
|
|
.table td {
|
|
display: block;
|
|
width: 100%;
|
|
}
|
|
|
|
.table td:nth-child(2) {
|
|
min-width: 0;
|
|
max-width: none;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
/* Surtout pas display:flex ici : le texte de la cellule deviendrait un élément
|
|
flex anonyme, avec min-width:auto — un titre long refuserait de se réduire et
|
|
sortirait de la carte, en entraînant toute la page. En bloc, l'intitulé passe
|
|
au-dessus de la valeur et la coupure de mots suffit. */
|
|
.table td {
|
|
display: block;
|
|
padding: 0.5rem 1rem;
|
|
border-bottom: none;
|
|
text-align: left;
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.table td[data-label]::before {
|
|
content: attr(data-label);
|
|
display: block;
|
|
font-size: 0.6875rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
color: rgb(107, 114, 128);
|
|
margin-bottom: 0.125rem;
|
|
}
|
|
|
|
/* `.table td { display: block }` l'emporte en spécificité sur `.rowActions` :
|
|
on le rétablit ici pour garder les actions sur une rangée. */
|
|
.table td.rowActions {
|
|
display: flex;
|
|
}
|
|
|
|
.rowActions {
|
|
justify-content: stretch;
|
|
padding-top: 0.75rem;
|
|
border-top: 1px solid rgb(243, 244, 246);
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.rowActions button {
|
|
flex: 1 1 8rem;
|
|
min-width: 0;
|
|
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. */
|