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:
+17
-17
@@ -7,7 +7,7 @@ type User = { id: string; email: string; full_name?: string; role: 'admin' | 'us
|
||||
type Document = { id: string; title: string; source: 'CIR' | 'SCCS'; ingredient?: string; date?: string }
|
||||
|
||||
type ScraperLog = { id: string; when: string; source: string; status: 'pending' | 'running' | 'success' | 'error' }
|
||||
type Stats = { total_documents?: number; cir?: number; sccs?: number; users?: number }
|
||||
type Stats = { total_documents?: number; cir_documents?: number; sccs_documents?: number; total_users?: number }
|
||||
|
||||
export default function Admin() {
|
||||
const [tab, setTab] = useState<'users'|'documents'|'stats'|'scrapers'>('users')
|
||||
@@ -184,10 +184,10 @@ export default function Admin() {
|
||||
<tbody>
|
||||
{sortedUsers.map(u=> (
|
||||
<tr key={u.id}>
|
||||
<td>{u.id}</td>
|
||||
<td>{u.email}</td>
|
||||
<td>{u.full_name||'-'}</td>
|
||||
<td><span className={u.role==='admin'?styles.badgeAdmin:styles.badgeUser}>{u.role}</span></td>
|
||||
<td data-label="ID">{u.id}</td>
|
||||
<td data-label="Email">{u.email}</td>
|
||||
<td data-label="Nom complet">{u.full_name||'-'}</td>
|
||||
<td data-label="Rôle"><span className={u.role==='admin'?styles.badgeAdmin:styles.badgeUser}>{u.role}</span></td>
|
||||
<td className={styles.rowActions}>
|
||||
<button className={styles.danger} onClick={()=>openConfirm('Supprimer cet utilisateur ?', ()=>deleteUser(u.id))}>Supprimer</button>
|
||||
<button onClick={()=>openConfirm(u.role==='admin'? 'Rétrograder cet utilisateur ?' : 'Passer admin ?', ()=>toggleRole(u))}>{u.role==='admin'?'Rétrograder':'Passer admin'}</button>
|
||||
@@ -212,9 +212,9 @@ export default function Admin() {
|
||||
<thead>
|
||||
<tr>
|
||||
<th onClick={()=>{ setSortKey('id'); setSortDir(sortDir==='asc'?'desc':'asc') }}>ID</th>
|
||||
<th onClick={()=>{ setSortKey('title'); setSortDir(sortDir==='asc'?'desc':'asc') }}>Title</th>
|
||||
<th onClick={()=>{ setSortKey('title'); setSortDir(sortDir==='asc'?'desc':'asc') }}>Titre</th>
|
||||
<th onClick={()=>{ setSortKey('source'); setSortDir(sortDir==='asc'?'desc':'asc') }}>Source</th>
|
||||
<th onClick={()=>{ setSortKey('ingredient'); setSortDir(sortDir==='asc'?'desc':'asc') }}>Ingredient</th>
|
||||
<th onClick={()=>{ setSortKey('ingredient'); setSortDir(sortDir==='asc'?'desc':'asc') }}>Ingrédient</th>
|
||||
<th onClick={()=>{ setSortKey('date'); setSortDir(sortDir==='asc'?'desc':'asc') }}>Date</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
@@ -222,11 +222,11 @@ export default function Admin() {
|
||||
<tbody>
|
||||
{sortedDocs.map(d=> (
|
||||
<tr key={d.id}>
|
||||
<td>{d.id}</td>
|
||||
<td>{d.title}</td>
|
||||
<td><span className={d.source==='CIR'?styles.badgeCIR:styles.badgeSCCS}>{d.source}</span></td>
|
||||
<td>{d.ingredient||'-'}</td>
|
||||
<td>{d.date||'-'}</td>
|
||||
<td data-label="ID">{d.id}</td>
|
||||
<td data-label="Titre">{d.title}</td>
|
||||
<td data-label="Source"><span className={d.source==='CIR'?styles.badgeCIR:styles.badgeSCCS}>{d.source}</span></td>
|
||||
<td data-label="Ingrédient">{d.ingredient||'-'}</td>
|
||||
<td data-label="Date">{d.date||'-'}</td>
|
||||
<td className={styles.rowActions}><button className={styles.danger} onClick={()=>openConfirm('Supprimer ce document ?', ()=>deleteDocument(d.id))}>Supprimer</button></td>
|
||||
</tr>
|
||||
))}
|
||||
@@ -242,16 +242,16 @@ export default function Admin() {
|
||||
<section>
|
||||
<div className={styles.cards}>
|
||||
<div className={styles.card}><FileText/><div className={styles.cardLabel}>Total documents</div><div className={styles.cardValue}>{stats?.total_documents ?? '-'}</div></div>
|
||||
<div className={styles.card}><Database/><div className={styles.cardLabel}>Documents CIR</div><div className={styles.cardValue}>{stats?.cir ?? '-'}</div></div>
|
||||
<div className={styles.card}><Database/><div className={styles.cardLabel}>Documents SCCS</div><div className={styles.cardValue}>{stats?.sccs ?? '-'}</div></div>
|
||||
<div className={styles.card}><Users/><div className={styles.cardLabel}>Total utilisateurs</div><div className={styles.cardValue}>{stats?.users ?? '-'}</div></div>
|
||||
<div className={styles.card}><Database/><div className={styles.cardLabel}>Documents CIR</div><div className={styles.cardValue}>{stats?.cir_documents ?? '-'}</div></div>
|
||||
<div className={styles.card}><Database/><div className={styles.cardLabel}>Documents SCCS</div><div className={styles.cardValue}>{stats?.sccs_documents ?? '-'}</div></div>
|
||||
<div className={styles.card}><Users/><div className={styles.cardLabel}>Total utilisateurs</div><div className={styles.cardValue}>{stats?.total_users ?? '-'}</div></div>
|
||||
</div>
|
||||
|
||||
<div className={styles.chartWrap}>
|
||||
<div className={styles.chartLabel}>CIR vs SCCS</div>
|
||||
<div className={styles.bars}>
|
||||
<div className={styles.barRow}><div className={styles.barTitle}>CIR</div><div className={styles.barFill} style={{width:`${(stats?.cir||0)/(Math.max(1, (stats?.cir||0)+(stats?.sccs||0))) * 100}%`}}></div><div className={styles.barVal}>{stats?.cir||0}</div></div>
|
||||
<div className={styles.barRow}><div className={styles.barTitle}>SCCS</div><div className={`${styles.barFill} ${styles.barFillAlt}`} style={{width:`${(stats?.sccs||0)/(Math.max(1, (stats?.cir||0)+(stats?.sccs||0))) * 100}%`}}></div><div className={styles.barVal}>{stats?.sccs||0}</div></div>
|
||||
<div className={styles.barRow}><div className={styles.barTitle}>CIR</div><div className={styles.barFill} style={{width:`${(stats?.cir_documents||0)/(Math.max(1, (stats?.cir_documents||0)+(stats?.sccs_documents||0))) * 100}%`}}></div><div className={styles.barVal}>{stats?.cir_documents||0}</div></div>
|
||||
<div className={styles.barRow}><div className={styles.barTitle}>SCCS</div><div className={`${styles.barFill} ${styles.barFillAlt}`} style={{width:`${(stats?.sccs_documents||0)/(Math.max(1, (stats?.cir_documents||0)+(stats?.sccs_documents||0))) * 100}%`}}></div><div className={styles.barVal}>{stats?.sccs_documents||0}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
+568
-44
@@ -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}
|
||||
.page {
|
||||
padding: 2rem;
|
||||
max-width: 80rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.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}
|
||||
/* ── En-tête et onglets ─────────────────────────────────────── */
|
||||
|
||||
.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}
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1.75rem;
|
||||
}
|
||||
|
||||
.empty{padding:12px;color:#64748b}
|
||||
.header h1 {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
color: rgb(17, 24, 39);
|
||||
line-height: 1.1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.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}
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.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}
|
||||
.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;
|
||||
}
|
||||
|
||||
.scraperControls{display:flex;gap:20px;align-items:flex-start}
|
||||
.logList{background:#fff;padding:12px;border-radius:8px}
|
||||
.status{font-weight:700}
|
||||
.tabs button:hover {
|
||||
background: rgb(249, 250, 251);
|
||||
border-color: rgb(209, 213, 219);
|
||||
}
|
||||
|
||||
.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}
|
||||
.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) {
|
||||
.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: 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{flex-direction:column}
|
||||
.tabs{flex-wrap:wrap}
|
||||
.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