mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
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:
7 files changed
+47
-159
No files matched your search
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}`}
|
||||||
|
|||||||
@@ -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`,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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
|
|
||||||
Reference in new issue
Block a user