Refonte UI : finitions (en-tête mobile, menu thème, libellés FR) et charte graphique

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ADkrVcVFTT14hbB148HYS
This commit is contained in:
Claude committed 2026-10-06 13:01:51 +00:00
1 parent 7a7188e508
commit 79114a741b
7 files changed
+47 -159

No files matched your search

+4 -4
View File
@@ -1,7 +1,7 @@
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { Link, useLocation } from "wouter"; import { Link, useLocation } from "wouter";
import Sidebar from "@/components/sidebar"; import Sidebar from "@/components/sidebar";
import { LogoMark } from "@/components/brand/logo"; import { Logo } from "@/components/brand/logo";
import { MobileNav } from "@/components/layout/mobile-nav"; import { MobileNav } from "@/components/layout/mobile-nav";
import { titleForPath } from "@/components/layout/nav-config"; import { titleForPath } from "@/components/layout/nav-config";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -15,10 +15,10 @@ export function AppShell({ children }: { children: ReactNode }) {
<Sidebar /> <Sidebar />
<div className="flex min-w-0 flex-1 flex-col"> <div className="flex min-w-0 flex-1 flex-col">
<header className="sticky top-0 z-30 flex h-14 items-center gap-3 border-b bg-card/90 px-4 backdrop-blur-lg lg:hidden"> <header className="sticky top-0 z-30 flex h-14 items-center gap-3 border-b bg-card/90 px-4 backdrop-blur-lg lg:hidden">
<Link href="/" aria-label="Accueil"> <Link href="/" aria-label="Accueil Social Flow">
<LogoMark size={30} /> <Logo size={30} subtitle={false} />
</Link> </Link>
<p className="truncate font-semibold">{titleForPath(location)}</p> <span className="sr-only">{titleForPath(location)}</span>
</header> </header>
<main className="flex-1 pb-24 lg:pb-0">{children}</main> <main className="flex-1 pb-24 lg:pb-0">{children}</main>
</div> </div>
+3 -3
View File
@@ -33,7 +33,7 @@ function NavLink({ item, active, collapsed }: { item: NavItem; active: boolean;
href={item.href} href={item.href}
aria-current={active ? "page" : undefined} aria-current={active ? "page" : undefined}
className={cn( className={cn(
"group relative flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors", "group relative flex items-center gap-3 rounded-lg px-3 py-[7px] text-sm font-medium transition-colors",
active active
? "bg-primary/10 text-primary" ? "bg-primary/10 text-primary"
: "text-muted-foreground hover:bg-sidebar-accent hover:text-foreground", : "text-muted-foreground hover:bg-sidebar-accent hover:text-foreground",
@@ -98,7 +98,7 @@ export default function Sidebar() {
<nav className={cn("flex-1 overflow-y-auto py-4 scrollbar-none", collapsed ? "px-3" : "px-4")} aria-label="Navigation principale"> <nav className={cn("flex-1 overflow-y-auto py-4 scrollbar-none", collapsed ? "px-3" : "px-4")} aria-label="Navigation principale">
{groups.map((group, gi) => ( {groups.map((group, gi) => (
<div key={group.label ?? gi} className={cn(gi > 0 && "mt-5")}> <div key={group.label ?? gi} className={cn(gi > 0 && "mt-4")}>
{group.label && {group.label &&
(collapsed ? ( (collapsed ? (
<div className="mx-auto mb-2 h-px w-6 bg-sidebar-border" /> <div className="mx-auto mb-2 h-px w-6 bg-sidebar-border" />
@@ -140,7 +140,7 @@ export default function Sidebar() {
)} )}
</button> </button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent side={collapsed ? "right" : "top"} align="start" className="w-60"> <DropdownMenuContent side={collapsed ? "right" : "top"} align="start" className="w-64">
<DropdownMenuLabel className="font-normal"> <DropdownMenuLabel className="font-normal">
<p className="text-sm font-medium">{session.username}</p> <p className="text-sm font-medium">{session.username}</p>
<p className="text-xs text-muted-foreground">{isAdmin ? "Administrateur" : "Utilisateur"}</p> <p className="text-xs text-muted-foreground">{isAdmin ? "Administrateur" : "Utilisateur"}</p>
+1 -1
View File
@@ -37,7 +37,7 @@ export function ThemeToggle({ className, compact = false }: { className?: string
title={label} title={label}
onClick={() => setTheme(value)} onClick={() => setTheme(value)}
className={cn( className={cn(
"flex flex-1 items-center justify-center gap-1.5 rounded-md px-2 py-1.5 text-xs font-medium text-muted-foreground transition-colors", "flex min-w-0 flex-1 items-center justify-center gap-1.5 rounded-md px-1.5 py-1.5 text-xs font-medium text-muted-foreground transition-colors",
current === value ? "bg-card text-foreground shadow-sm" : "hover:text-foreground", current === value ? "bg-card text-foreground shadow-sm" : "hover:text-foreground",
)} )}
data-testid={`theme-${value}`} data-testid={`theme-${value}`}
+1 -1
View File
@@ -68,7 +68,7 @@ export default function AudioAdmin() {
const successCount = data.results?.filter((r: any) => r.success).length ?? 0; const successCount = data.results?.filter((r: any) => r.success).length ?? 0;
const failCount = data.results?.filter((r: any) => !r.success).length ?? 0; const failCount = data.results?.filter((r: any) => !r.success).length ?? 0;
toast({ toast({
title: "Upload terminé", title: "Import terminé",
description: failCount > 0 description: failCount > 0
? `${successCount} fichier(s) ajouté(s), ${failCount} échec(s)` ? `${successCount} fichier(s) ajouté(s), ${failCount} échec(s)`
: `${successCount} fichier(s) ajouté(s) à la bibliothèque`, : `${successCount} fichier(s) ajouté(s) à la bibliothèque`,
+1 -1
View File
@@ -783,7 +783,7 @@ export default function NewPost() {
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Icon className={`h-5 w-5 ${postType === value ? 'text-primary' : 'text-muted-foreground'}`} /> <Icon className={`h-5 w-5 ${postType === value ? 'text-primary' : 'text-muted-foreground'}`} />
<RadioGroupItem value={value} id={`format-${value}`} className="sr-only sm:not-sr-only" /> <RadioGroupItem value={value} id={`format-${value}`} className="hidden sm:block" />
</div> </div>
<div> <div>
<p className="text-sm font-medium">{label}</p> <p className="text-sm font-medium">{label}</p>
+6 -6
View File
@@ -278,7 +278,7 @@ export default function Settings() {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['/api/cloudinary/config'] }); queryClient.invalidateQueries({ queryKey: ['/api/cloudinary/config'] });
toast({ toast({
title: "Logo uploadé", title: "Logo enregistré",
description: "Votre logo a été enregistré avec succès", description: "Votre logo a été enregistré avec succès",
}); });
setLogoFile(null); setLogoFile(null);
@@ -286,7 +286,7 @@ export default function Settings() {
onError: (error: Error) => { onError: (error: Error) => {
toast({ toast({
title: "Erreur", title: "Erreur",
description: error.message || "Impossible d'uploader le logo", description: error.message || "Impossible d'importer le logo",
variant: "destructive", variant: "destructive",
}); });
}, },
@@ -512,7 +512,7 @@ export default function Settings() {
Logo de l'entreprise Logo de l'entreprise
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
Uploadez votre logo pour l'ajouter aux images éditées Importez votre logo pour l'ajouter aux images éditées
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
@@ -545,7 +545,7 @@ export default function Settings() {
onClick={() => document.getElementById('logo-upload-replace')?.click()} onClick={() => document.getElementById('logo-upload-replace')?.click()}
> >
<Upload className="w-4 h-4" /> <Upload className="w-4 h-4" />
{uploadLogoMutation.isPending ? "Upload en cours..." : "Remplacer le logo"} {uploadLogoMutation.isPending ? "Import en cours…" : "Remplacer le logo"}
</Button> </Button>
</Label> </Label>
<input <input
@@ -567,7 +567,7 @@ export default function Settings() {
<div className="border-2 border-dashed border-border rounded-lg p-8 text-center"> <div className="border-2 border-dashed border-border rounded-lg p-8 text-center">
<Upload className="w-12 h-12 mx-auto mb-4 text-muted-foreground" /> <Upload className="w-12 h-12 mx-auto mb-4 text-muted-foreground" />
<p className="text-sm text-muted-foreground mb-4"> <p className="text-sm text-muted-foreground mb-4">
Aucun logo uploadé. Choisissez une image PNG ou JPG. Aucun logo. Choisissez une image PNG ou JPG.
</p> </p>
<Label htmlFor="logo-upload" className="cursor-pointer"> <Label htmlFor="logo-upload" className="cursor-pointer">
<Button <Button
@@ -577,7 +577,7 @@ export default function Settings() {
onClick={() => document.getElementById('logo-upload')?.click()} onClick={() => document.getElementById('logo-upload')?.click()}
> >
<Upload className="w-4 h-4" /> <Upload className="w-4 h-4" />
{uploadLogoMutation.isPending ? "Upload en cours..." : "Uploader un logo"} {uploadLogoMutation.isPending ? "Import en cours…" : "Importer un logo"}
</Button> </Button>
</Label> </Label>
<input <input
+31 -143
View File
@@ -1,155 +1,43 @@
# Social Flow - Design Guidelines # Social Flow - Design Guidelines
## Design Approach Interface sobre et lisible, en français, pensée d'abord pour être comprise sans formation.
Toutes les couleurs passent par les tokens de `client/src/index.css` (modes clair et sombre).
**Selected Framework:** Design System Approach inspired by modern SaaS dashboards (Linear, Buffer, Notion) with Material Design principles for data-rich interfaces. ## Identité
**Core Philosophy:** Professional productivity tool prioritizing clarity, efficiency, and visual hierarchy in dark mode with sophisticated blue/purple branding. - **Logo** : `components/brand/logo.tsx` (`<Logo />`, `<LogoMark />`) — deux ondes qui s'écoulent vers un point, sur un carré arrondi en dégradé indigo → corail. Même dessin dans `client/public/favicon.svg`.
- **Primaire** : indigo `#4F46E5` (`primary`) — actions, éléments actifs, focus.
- **Accent** : corail `#F97362` (`brand-accent`) — uniquement dans le dégradé de marque.
- **Dégradé de marque** (`.gradient-brand`, bouton `variant="brand"`) : réservé au logo et à **une seule** action principale par écran (publier, créer, se connecter).
- **Statuts** : `success` (publié, actif), `warning` (bientôt expiré), `destructive` (échec, suppression), `info` (en cours).
- **Plateformes** : `facebook`, `instagram`, `tiktok` — via `components/platform-icon.tsx`.
- Ne jamais utiliser de couleurs Tailwind brutes (`bg-blue-500`, `#1877F2`…) ni `text-white` sur une surface de l'interface.
## Color Palette ## Typographie
**Dark Mode (Primary Theme):** - Inter (400–700), JetBrains Mono pour les identifiants et le SQL.
- Background Base: 222 20% 8% - Titre de page : `PageHeader` (`text-2xl sm:text-[28px] font-semibold`).
- Surface Elevated: 222 18% 12% - Titre de carte : `CardTitle` (`text-base font-semibold`), description en `text-muted-foreground`.
- Surface Card: 222 16% 15%
- Border Subtle: 222 12% 20%
**Brand Colors:** ## Mise en page
- Primary Blue: 215 85% 58%
- Primary Purple: 265 75% 62%
- Gradient Accent: Linear gradient from Primary Blue to Primary Purple
**Functional Colors:** - **Shell** : `components/layout/app-shell.tsx` — sidebar groupée sur desktop (≥ lg), en-tête + barre de navigation basse sur mobile (Accueil, Calendrier, **Créer**, Médias, Menu). La navigation est définie une seule fois dans `components/layout/nav-config.ts`.
- Success Green: 142 76% 48% - **Contenu** : chaque page commence par `<Page width="narrow|default|wide">` puis `<PageHeader icon title description actions />`.
- Warning Orange: 35 95% 58% - `narrow` (max-w-3xl) : formulaires simples
- Error Red: 0 85% 62% - `default` (max-w-6xl) : la plupart des pages
- Info Cyan: 195 85% 55% - `wide` (1400px) : tableau de bord, calendrier, médiathèque
- Une seule version responsive par écran : pas de pages mobiles séparées.
**Text Hierarchy:** ## Composants
- Primary Text: 0 0% 95%
- Secondary Text: 222 10% 65%
- Tertiary Text: 222 8% 48%
## Typography - **Cartes** : `Card` sans surcharge de style (`rounded-xl border shadow-soft`).
- **États vides** : `components/empty-state.tsx` avec une action suggérée.
- **Parcours de création** : `components/stepper.tsx` (`Stepper` + `StepNavigation`), utilisé par Publication et Reel. On explique toujours pourquoi une étape est verrouillée.
- **Choix exclusifs** : cartes sélectionnables (`RadioGroup` dans un `label` bordé), pas de listes déroulantes quand il y a 2 à 4 options.
- **Suppressions** : toujours confirmées par `AlertDialog`, jamais `confirm()`.
- **Badges** : variantes `success`, `warning`, `info`, `danger`, `muted`.
**Font Family:** ## Rédaction
- Primary: 'Inter' from Google Fonts (400, 500, 600, 700)
- Monospace: 'JetBrains Mono' for timestamps and metrics
**Scale:** - Verbes d'action clairs : « Importer », « Programmer », « Connecter un compte ».
- Headings H1: text-3xl font-semibold - Pas d'anglicismes (« Uploader », « Token ») ni de jargon technique visible par défaut : les détails vont dans un bloc « Besoin d'aide ? » repliable.
- Headings H2: text-2xl font-semibold
- Headings H3: text-xl font-medium
- Body Large: text-base
- Body: text-sm
- Captions: text-xs text-secondary
## Layout System
**Spacing Primitives:** Tailwind units of 2, 4, 6, 8, 12, 16 (e.g., p-4, gap-6, mt-8)
**Grid Structure:**
- Sidebar: Fixed width 280px (w-70) on desktop, collapsible on mobile
- Main Content: Flexible with max-w-7xl container
- Dashboard Grid: grid-cols-1 md:grid-cols-2 lg:grid-cols-3 with gap-6
**Container Widths:**
- Dashboard sections: max-w-7xl with px-6 to px-8
- Form containers: max-w-2xl
- Content cards: Full width within grid columns
## Component Library
### Sidebar Navigation
- Dark surface (Background Base) with subtle border-right
- Logo + app name at top (h-16)
- Navigation items with icons (Heroicons) + labels
- Active state: Primary gradient background with 12% opacity, blue text
- Hover: Surface Elevated background
- Bottom section: User profile card with avatar, name, settings icon
- Spacing: py-2 between items, px-4 horizontal padding
### Dashboard Cards
- Background: Surface Card with rounded-xl
- Border: 1px solid Border Subtle
- Shadow: Soft shadow (shadow-lg with low opacity)
- Padding: p-6
- Header: Flex justify-between with title (text-lg font-semibold) + action button
- Content spacing: space-y-4 internally
### Post Scheduling Interface
- Calendar view: Week/month grid with date cells
- Post cards: Compact cards showing thumbnail + caption preview + platform badges (FB/IG)
- Drag-and-drop visual indicators
- Time slots: Hourly breakdown with scheduled posts
### Analytics Cards
- Metric cards: Large number (text-3xl font-bold) + label + trend indicator
- Charts: Use Chart.js with gradient fills matching brand colors
- Grid layout for KPIs: 4 columns on desktop, responsive down
### Forms & Inputs
- Input fields: Surface Elevated background, border Border Subtle, rounded-lg, px-4 py-3
- Focus state: Primary Blue border, subtle glow
- Labels: text-sm font-medium mb-2, Secondary Text color
- Textarea: min-h-32 for post composition
- File upload: Dashed border drop zone with center icon and text
### Buttons
- Primary: Gradient background (blue to purple), text-white, rounded-lg, px-6 py-3, font-medium
- Secondary: Border Primary Blue, text Primary Blue, transparent background
- Ghost: Transparent, hover Surface Elevated
- Icon buttons: w-10 h-10, rounded-lg, center content
### Platform Badges
- Small pills (px-3 py-1, rounded-full, text-xs font-medium)
- Facebook: Blue background (215 85% 58% with 15% opacity), blue text
- Instagram: Purple background (265 75% 62% with 15% opacity), purple text
### Status Indicators
- Dots with colors: w-2 h-2 rounded-full
- Scheduled: Primary Blue
- Published: Success Green
- Failed: Error Red
- Draft: Tertiary Text
### Data Tables
- Header row: Surface Elevated background, font-medium, uppercase text-xs
- Rows: Border-bottom subtle, hover Surface Card
- Padding: px-6 py-4
- Alternating backgrounds for better readability
## Animations
**Minimal & Purposeful:**
- Page transitions: Fade-in only (150ms)
- Card hover: Subtle lift (translateY -2px, 200ms)
- Button interactions: Scale 0.98 on active
- Navigation: Smooth color transitions (200ms)
- No distracting scroll animations or complex effects
## Images
**Hero Section:** None - Dashboard-first interface prioritizing immediate utility
**Avatar & Thumbnails:**
- User avatars: w-10 h-10, rounded-full with border
- Post thumbnails: aspect-video, rounded-lg, object-cover
- Platform logos: Use official brand SVGs at 20x20 or 24x24
**Empty States:**
- Illustration placeholders for "No posts scheduled", "No analytics data"
- Center aligned with icon, heading, and CTA button
- Subtle grayscale illustrations maintaining professional tone
## Key Screens Layout
**Dashboard:** 3-column grid with Overview metrics (top row), Recent Posts card, Upcoming Schedule card, Quick Analytics
**Calendrier:** Full-width calendar component, left sidebar filter (platforms, status), right panel for selected post details
**Nouvelle Publication:** Center form (max-w-2xl), left preview panel showing platform-specific mockups, sticky publish/schedule buttons
**Analytiques:** Metric cards grid (4 cols), followed by full-width charts for engagement over time, best posting times heatmap
**Paramètres:** Two-column layout, left navigation tabs, right content panels