mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-24 07:38:04 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2da125eca2 |
No files matched your search
@@ -268,6 +268,17 @@ même raison.
|
||||
droit à l'image) et `/backend/espace/promotions` (dépôt et suivi des promos,
|
||||
les offres **en ligne** en tête). `EspaceHeader` porte le bandeau et les
|
||||
onglets ; toute action qui touche l'espace revalide les deux chemins.
|
||||
- **Pas de barre latérale hors staff** : `BackendShell` branche sur
|
||||
`isStaff(user.role)`. Chez un adhérent la barre ne porterait que la section
|
||||
« Côté adhérent », c'est-à-dire les trois entrées déjà rendues en onglets par
|
||||
`EspaceHeader` — deux menus pour la même chose. Il ne reste qu'un bandeau de
|
||||
marque, aligné sur la colonne de contenu (`max-width` 1040). Conséquence à ne
|
||||
pas perdre de vue : sous 1024px les actions du compte vivaient **dans le
|
||||
tiroir** ; sans tiroir, `.backend-header--plain` rétablit leur affichage dans
|
||||
l'entête, sinon la déconnexion deviendrait inatteignable sur téléphone.
|
||||
`tests/backend-nav.test.ts` verrouille les deux points. Le staff garde la
|
||||
barre : elle y est la navigation principale, et « Côté adhérent » son seul
|
||||
chemin vers son propre espace.
|
||||
- La catégorie d'une promotion est un **type de produit ou de service**
|
||||
(`src/lib/promo-categories.ts`, groupes pour `<optgroup>`), pas le métier de
|
||||
l'adhérent ; `defaultPromoCategory(slug)` pré-sélectionne depuis le métier.
|
||||
|
||||
@@ -4,7 +4,7 @@ import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { BackendIcon, type BackendIconName } from "@/components/BackendIcons";
|
||||
import { can, ROLE_LABELS } from "@/lib/rbac";
|
||||
import { can, isStaff, ROLE_LABELS } from "@/lib/rbac";
|
||||
import type { AppRole } from "@/types/next-auth";
|
||||
import { doSignOut } from "./actions";
|
||||
|
||||
@@ -192,6 +192,43 @@ export function BackendShell({
|
||||
</form>
|
||||
);
|
||||
|
||||
/**
|
||||
* Un adhérent n'a pas de barre latérale : elle ne porterait que la section
|
||||
* « Côté adhérent », c'est-à-dire les trois entrées déjà présentes en onglets
|
||||
* sous le bandeau de l'espace. Deux menus pour la même chose, dont un qui
|
||||
* mange un quart de l'écran. Il ne reste donc qu'un bandeau de marque, et le
|
||||
* contenu prend toute la largeur — recentré pour ne pas s'étirer.
|
||||
*
|
||||
* Le staff la garde : chez lui elle est la navigation principale, et
|
||||
* « Côté adhérent » y est le seul chemin vers son propre espace.
|
||||
*/
|
||||
if (!isStaff(user.role)) {
|
||||
return (
|
||||
<div className="backend backend--plain" style={{ fontFamily: "'Public Sans',sans-serif", color: "#33291D" }}>
|
||||
<header className="backend-header backend-header--plain">
|
||||
<Link href="/backend/espace/informations" className="backend-brand">
|
||||
<span className="backend-brand__mark">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/assets/logo.png" alt="Plein R" style={{ height: 34, width: "auto", display: "block" }} />
|
||||
</span>
|
||||
<span className="backend-brand__text">
|
||||
<span className="font-display backend-brand__name">Plein R</span>
|
||||
<span className="backend-brand__sub">Espace adhérent</span>
|
||||
</span>
|
||||
</Link>
|
||||
<div className="backend-header__actions">
|
||||
{siteLink}
|
||||
{signOut}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Le bandeau de l'espace porte déjà le nom et les onglets : répéter
|
||||
ici le titre de la page ferait un troisième étage d'en-tête. */}
|
||||
<div className="backend-content backend-content--plain">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="backend" style={{ fontFamily: "'Public Sans',sans-serif", color: "#33291D" }}>
|
||||
{menuOpen && (
|
||||
|
||||
@@ -850,6 +850,81 @@ img {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* ---- Backend sans barre latérale (espace adhérent) ----
|
||||
Un adhérent n'a que les trois onglets de son espace : une barre latérale
|
||||
les répéterait. `.backend` étant une rangée flex, la variante repasse en
|
||||
bloc pour que l'entête et le contenu s'empilent simplement. */
|
||||
.backend--plain {
|
||||
display: block;
|
||||
}
|
||||
/* L'entête s'aligne sur la colonne de contenu : sans cela le logo collerait
|
||||
au bord de l'écran pendant que le contenu reste centré. */
|
||||
.backend-header--plain {
|
||||
padding: 14px max(24px, calc((100% - 1040px) / 2));
|
||||
}
|
||||
.backend-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
margin-right: auto;
|
||||
min-width: 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
.backend-brand__mark {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
padding: 5px;
|
||||
border-radius: 11px;
|
||||
background: #fff;
|
||||
border: 1px solid #e6dcc6;
|
||||
}
|
||||
.backend-brand__text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.15;
|
||||
min-width: 0;
|
||||
}
|
||||
.backend-brand__name {
|
||||
font-size: 17px;
|
||||
font-weight: 800;
|
||||
color: var(--ink-strong);
|
||||
}
|
||||
.backend-brand__sub {
|
||||
font-size: 11.5px;
|
||||
color: var(--muted-3);
|
||||
}
|
||||
/* Sans barre latérale, il n'y a plus de tiroir où loger les actions sur
|
||||
téléphone : elles restent dans l'entête à toutes les largeurs. */
|
||||
.backend-header--plain .backend-header__actions {
|
||||
display: flex;
|
||||
}
|
||||
.backend-content--plain {
|
||||
width: 100%;
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.backend-header--plain {
|
||||
padding: 11px 16px;
|
||||
}
|
||||
.backend-content--plain {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Le nom de marque s'efface avant les actions : le logo suffit à situer la
|
||||
page, deux boutons tronqués ne servent à rien. */
|
||||
@media (max-width: 560px) {
|
||||
.backend-brand__text {
|
||||
display: none;
|
||||
}
|
||||
.backend-header--plain .backend-action {
|
||||
padding: 8px 11px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
||||
@@ -42,3 +42,41 @@ test("chaque icône déclarée dans le type est dessinée", () => {
|
||||
assert.ok(drawn.has(name), `l'icône « ${name} » est déclarée mais pas dessinée`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Un adhérent n'a pas de barre latérale : elle ne porterait que la section
|
||||
* « Côté adhérent », déjà rendue en onglets par `EspaceHeader`. Le test tient
|
||||
* les deux bouts — la coquille doit brancher sur le rôle, et sa variante sans
|
||||
* barre ne doit rendre ni `aside`, ni bouton de tiroir.
|
||||
*/
|
||||
const memberStart = shell.indexOf("if (!isStaff(user.role))");
|
||||
// La tranche s'arrête à la coquille du staff, sinon elle l'embarquerait et
|
||||
// le test verrait son <aside> — il passerait pour un échec de la variante.
|
||||
const staffStart = shell.indexOf('<div className="backend" style', memberStart);
|
||||
const memberBranch = shell.slice(memberStart, staffStart);
|
||||
|
||||
test("la coquille distingue le staff de l'adhérent", () => {
|
||||
assert.ok(shell.includes("isStaff"), "BackendShell ne consulte plus le rôle");
|
||||
assert.ok(memberStart >= 0, "la branche sans barre latérale a disparu");
|
||||
assert.ok(staffStart > memberStart, "la coquille du staff ne suit plus la branche adhérent");
|
||||
});
|
||||
|
||||
test("la variante adhérent ne rend ni barre latérale ni tiroir", () => {
|
||||
assert.ok(!/<aside/.test(memberBranch), "un <aside> subsiste dans la variante adhérent");
|
||||
assert.ok(!memberBranch.includes("backend-burger"), "le bouton de tiroir subsiste sans tiroir à ouvrir");
|
||||
assert.ok(!memberBranch.includes("sidebar"), "une classe de barre latérale subsiste");
|
||||
});
|
||||
|
||||
test("sans tiroir, les actions du compte restent dans l'entête", () => {
|
||||
// Sur téléphone elles vivaient dans la barre : sans elle, se déconnecter
|
||||
// deviendrait impossible si la règle d'affichage n'était pas rétablie.
|
||||
assert.ok(memberBranch.includes("{signOut}"), "la déconnexion a disparu de l'entête adhérent");
|
||||
assert.ok(memberBranch.includes("{siteLink}"), "le retour au site a disparu de l'entête adhérent");
|
||||
|
||||
const css = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
|
||||
assert.match(
|
||||
css,
|
||||
/\.backend-header--plain\s+\.backend-header__actions\s*\{\s*display:\s*flex/,
|
||||
"les actions restent masquées sous 1024px, où le tiroir n'existe plus",
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user