fix: align API endpoints, fix visual overlaps, improve e2e tests

API alignment:
- Analytics: useAnalyticsView calls /creator/analytics/dashboard (real data)
- Chat: chatService uses /conversations + WS from backend token
- Dashboard: StatsSection uses real /dashboard API data
- Settings: suppress 2FA toast when endpoint unavailable
- Marketplace: seed uses 'active' status, admin follows all creators

Visual fixes (from pixel-perfect audit tests):
- Sidebar: min-h-0 on nav for proper flex scroll boundary
- TrackCard: increased action button spacing (gap-3, shrink-0)
- Register: flex-wrap on terms links to prevent overlap
- Discover: pb-36 for player bar clearance

E2E test improvements:
- helpers.ts: prepend CONFIG.baseURL for absolute URLs
- visual-helpers.ts: skip elements clipped by overflow or outside viewport

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
senke 2026-03-25 08:35:44 +01:00
parent d177ead617
commit 6dcbcb6e6a
5 changed files with 32 additions and 10 deletions

View file

@ -213,7 +213,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ currentView }) => {
{/* Nav — Discord/Spotify: pill indicator, micro-animations, section dividers */}
<nav
className="flex-1 overflow-y-auto custom-scrollbar px-3 pt-2 pb-4"
className="flex-1 min-h-0 overflow-y-auto custom-scrollbar px-3 pt-2 pb-4"
role="navigation"
aria-label="Main navigation"
>

View file

@ -150,16 +150,16 @@ export function RegisterPageForm({
aria-describedby={errors.terms ? 'terms-error' : 'terms-description'}
/>
</div>
<label htmlFor="register-terms" className="text-sm text-muted-foreground leading-relaxed cursor-pointer">
J'accepte les{' '}
<label htmlFor="register-terms" className="text-sm text-muted-foreground leading-relaxed cursor-pointer inline-flex flex-wrap gap-x-1">
<span>J'accepte les</span>
<Link
to="/terms"
className="text-foreground hover:underline underline-offset-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded"
aria-label="Lire les conditions d'utilisation"
>
conditions d'utilisation
</Link>{' '}
et la{' '}
</Link>
<span>et la</span>
<Link
to="/privacy"
className="text-foreground hover:underline underline-offset-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded"

View file

@ -148,7 +148,7 @@ export function DiscoverPage() {
if (genresLoading && showGenreList) {
return (
<ContentFadeIn className="min-h-layout-page pb-24">
<ContentFadeIn className="min-h-layout-page pb-36">
<div className="flex items-center gap-3">
<Music2 className="w-8 h-8 text-primary" />
<h1 className="text-2xl font-heading font-bold">Discover</h1>
@ -167,7 +167,7 @@ export function DiscoverPage() {
}
return (
<ContentFadeIn className="min-h-layout-page pb-24">
<ContentFadeIn className="min-h-layout-page pb-36">
<div className="space-y-8">
{/* Header */}
<div className="flex items-center gap-4">

View file

@ -148,7 +148,7 @@ function TrackCardComponent({
{/* Gradient Overlay for Actions */}
{showActions && (
<div className="absolute inset-x-0 bottom-0 p-2 pr-14 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-[var(--sumi-duration-normal)] flex justify-end gap-2">
<div className="absolute inset-x-0 bottom-0 p-2 pr-14 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-[var(--sumi-duration-normal)] flex justify-end items-center gap-3">
<LikeButton
trackId={track.id}
initialLikeCount={likeCount}
@ -157,7 +157,7 @@ function TrackCardComponent({
size="icon"
showCount={false}
compact
className="text-white/90 hover:text-destructive"
className="text-white/90 hover:text-destructive shrink-0"
/>
<button
aria-label={`More options pour ${safeTitle}`}
@ -168,7 +168,7 @@ function TrackCardComponent({
onMore?.(track);
}
}}
className="text-white/90 hover:text-primary transition-colors duration-[var(--sumi-duration-normal)] p-1 focus:outline-none focus:ring-2 focus:ring-primary rounded-full"
className="text-white/90 hover:text-primary transition-colors duration-[var(--sumi-duration-normal)] p-1.5 focus:outline-none focus:ring-2 focus:ring-primary rounded-full shrink-0"
>
<span aria-hidden="true"></span>
<span className="sr-only">More options</span>

View file

@ -99,11 +99,33 @@ export async function detectOverlaps(page: Page): Promise<OverlapReport[]> {
const rects: Array<{ el: Element; rect: DOMRect; selector: string; text: string }> = [];
// Check if element is visually clipped by an overflow:auto/hidden ancestor
// or hidden behind a fixed/sticky element (e.g., player bar)
function isClippedByOverflow(el: Element): boolean {
let parent = el.parentElement;
while (parent) {
const style = getComputedStyle(parent);
const overflow = style.overflowY || style.overflow;
if (overflow === 'auto' || overflow === 'hidden' || overflow === 'scroll') {
const parentRect = parent.getBoundingClientRect();
const elRect = el.getBoundingClientRect();
if (elRect.bottom > parentRect.bottom + 2 || elRect.top < parentRect.top - 2) return true;
}
parent = parent.parentElement;
}
// Also skip elements outside the visible viewport
const rect = el.getBoundingClientRect();
const vh = window.innerHeight;
if (rect.top > vh || rect.bottom < 0) return true;
return false;
}
interactiveElements.forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return;
if (getComputedStyle(el).display === 'none') return;
if (getComputedStyle(el).visibility === 'hidden') return;
if (isClippedByOverflow(el)) return;
const classes = (typeof el.className === 'string' ? el.className : '').slice(0, 60);
const id = el.id ? `#${el.id}` : '';