-
-
- {stat.change}
-
-
{stat.changeLabel}
+
+ {stat.value}
+
+
{stat.label}
+ {stat.change ? (
+
+
+
+ {stat.change}
+
+ {stat.changeLabel}
- )}
-
- {stat.info && (
-
- {stat.info}
-
- )}
+ ) : stat.info ? (
+
{stat.info}
+ ) : null}
+ >
+ );
+ const cls = "block rounded-xl border bg-card p-4 shadow-soft sm:p-5";
+ return stat.href ? (
+
+ {body}
+
+ ) : (
+
+ {body}
);
})}
diff --git a/client/src/components/theme-provider.tsx b/client/src/components/theme-provider.tsx
new file mode 100644
index 0000000..daba5ff
--- /dev/null
+++ b/client/src/components/theme-provider.tsx
@@ -0,0 +1,51 @@
+import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes";
+import { Monitor, Moon, Sun } from "lucide-react";
+import { cn } from "@/lib/utils";
+import type { ReactNode } from "react";
+
+export function ThemeProvider({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+const OPTIONS = [
+ { value: "light", label: "Clair", icon: Sun },
+ { value: "dark", label: "Sombre", icon: Moon },
+ { value: "system", label: "Système", icon: Monitor },
+] as const;
+
+/** Sélecteur segmenté clair / sombre / système. */
+export function ThemeToggle({ className, compact = false }: { className?: string; compact?: boolean }) {
+ const { theme, setTheme } = useTheme();
+ const current = theme ?? "system";
+
+ return (
+
+ {OPTIONS.map(({ value, label, icon: Icon }) => (
+
+ ))}
+
+ );
+}
diff --git a/client/src/components/topbar.tsx b/client/src/components/topbar.tsx
deleted file mode 100644
index cbc425f..0000000
--- a/client/src/components/topbar.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Menu, Search, ChevronLeft } from "lucide-react";
-import { Input } from "@/components/ui/input";
-import { useLocation, Link } from "wouter";
-import { Button } from "@/components/ui/button";
-
-interface TopBarProps {
- onMenuClick?: () => void;
- hideMenuButton?: boolean;
-}
-
-export default function TopBar({ onMenuClick, hideMenuButton = false }: TopBarProps) {
- const [location] = useLocation();
- const showBackButton = location !== "/";
-
- return (
-
-
-
-
- {showBackButton ? (
-
-
-
- ) : (
- !hideMenuButton && (
-
- )
- )}
-
-
-
-
-
- );
-}
diff --git a/client/src/components/ui/badge.tsx b/client/src/components/ui/badge.tsx
index f000e3e..c93e5fd 100644
--- a/client/src/components/ui/badge.tsx
+++ b/client/src/components/ui/badge.tsx
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium whitespace-nowrap transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
@@ -15,6 +15,11 @@ const badgeVariants = cva(
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
+ success: "border-transparent bg-success/15 text-success",
+ warning: "border-transparent bg-warning/15 text-warning-foreground dark:text-warning",
+ info: "border-transparent bg-info/15 text-info",
+ danger: "border-transparent bg-destructive/15 text-destructive",
+ muted: "border-transparent bg-muted text-muted-foreground",
},
},
defaultVariants: {
diff --git a/client/src/components/ui/button.tsx b/client/src/components/ui/button.tsx
index c8cbf29..1857424 100644
--- a/client/src/components/ui/button.tsx
+++ b/client/src/components/ui/button.tsx
@@ -5,25 +5,27 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background transition-all active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
+ brand:
+ "gradient-brand text-white shadow-md shadow-primary/20 hover:opacity-95 hover:shadow-lg hover:shadow-primary/25",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
+ "border border-input bg-card shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
- default: "h-11 px-4 py-2",
- sm: "h-11 rounded-md px-3",
- lg: "h-12 rounded-md px-8",
- icon: "h-11 w-11",
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-lg px-6 text-[15px]",
+ icon: "h-10 w-10",
},
},
defaultVariants: {
diff --git a/client/src/components/ui/card.tsx b/client/src/components/ui/card.tsx
index f62edea..4333daf 100644
--- a/client/src/components/ui/card.tsx
+++ b/client/src/components/ui/card.tsx
@@ -9,7 +9,7 @@ const Card = React.forwardRef<
(({ className, ...props }, ref) => (
))
@@ -36,7 +36,7 @@ const CardTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
-
+
))
CardContent.displayName = "CardContent"
@@ -70,7 +70,7 @@ const CardFooter = React.forwardRef<
>(({ className, ...props }, ref) => (
))
diff --git a/client/src/components/ui/input.tsx b/client/src/components/ui/input.tsx
index 68551b9..f76e3eb 100644
--- a/client/src/components/ui/input.tsx
+++ b/client/src/components/ui/input.tsx
@@ -8,7 +8,7 @@ const Input = React.forwardRef
>(
span]:line-clamp-1",
+ "flex h-10 w-full items-center justify-between rounded-lg border border-input bg-card px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-ring/30 focus:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}
diff --git a/client/src/components/ui/tabs.tsx b/client/src/components/ui/tabs.tsx
index f57fffd..0f81b86 100644
--- a/client/src/components/ui/tabs.tsx
+++ b/client/src/components/ui/tabs.tsx
@@ -12,7 +12,7 @@ const TabsList = React.forwardRef<
({
+ queryKey: ["/api/auth/session"],
+ retry: false,
+ });
+ return { ...query, session: query.data, isAdmin: query.data?.role === "admin" };
+}
+
+export function useLogout() {
+ const [, setLocation] = useLocation();
+ const { toast } = useToast();
+ return useMutation({
+ mutationKey: [AUTH_MUTATION, "logout"],
+ mutationFn: async () => {
+ const res = await apiRequest("POST", "/api/auth/logout", {});
+ return await res.json();
+ },
+ onSuccess: () => {
+ queryClient.clear();
+ setLocation("/login");
+ toast({ title: "Déconnecté", description: "À bientôt !" });
+ },
+ });
+}
diff --git a/client/src/hooks/useIsMobile.ts b/client/src/hooks/useIsMobile.ts
deleted file mode 100644
index a68bceb..0000000
--- a/client/src/hooks/useIsMobile.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { useState, useEffect } from 'react';
-
-/**
- * Hook to detect if the user is on a mobile device
- * Uses both media query and user agent for robust detection
- *
- * @param breakpoint - The max-width breakpoint for mobile (default: 768px)
- * @returns boolean - true if mobile, false if desktop
- */
-export function useIsMobile(breakpoint: number = 768): boolean {
- const [isMobile, setIsMobile] = useState(() => {
- // Initial detection (SSR-safe)
- if (typeof window === 'undefined') return false;
-
- // Check media query
- const mediaMatch = window.matchMedia(`(max-width: ${breakpoint}px)`).matches;
-
- // Check user agent for mobile devices
- const userAgent = navigator.userAgent.toLowerCase();
- const mobileKeywords = ['android', 'webos', 'iphone', 'ipad', 'ipod', 'blackberry', 'windows phone'];
- const isUserAgentMobile = mobileKeywords.some(keyword => userAgent.includes(keyword));
-
- // Return true if either media query or user agent indicates mobile
- return mediaMatch || isUserAgentMobile;
- });
-
- useEffect(() => {
- // Create media query list
- const mediaQueryList = window.matchMedia(`(max-width: ${breakpoint}px)`);
-
- // Handler for media query changes
- const handleChange = (e: MediaQueryListEvent) => {
- setIsMobile(e.matches);
- };
-
- // Listen for changes (modern browsers)
- if (mediaQueryList.addEventListener) {
- mediaQueryList.addEventListener('change', handleChange);
- } else {
- // Fallback for older browsers
- mediaQueryList.addListener(handleChange);
- }
-
- // Cleanup
- return () => {
- if (mediaQueryList.removeEventListener) {
- mediaQueryList.removeEventListener('change', handleChange);
- } else {
- mediaQueryList.removeListener(handleChange);
- }
- };
- }, [breakpoint]);
-
- return isMobile;
-}
-
-/**
- * Hook to detect device type with more granularity
- * @returns object with device type information
- */
-export function useDeviceType() {
- const isMobile = useIsMobile(768);
- const isTablet = useIsMobile(1024) && !useIsMobile(768);
- const isDesktop = !useIsMobile(1024);
-
- return {
- isMobile,
- isTablet,
- isDesktop,
- deviceType: isMobile ? 'mobile' : isTablet ? 'tablet' : 'desktop'
- } as const;
-}
diff --git a/client/src/index.css b/client/src/index.css
index 5802a06..348dfea 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -2,106 +2,132 @@
@tailwind components;
@tailwind utilities;
+/*
+ * Social Flow — design tokens.
+ * Toutes les couleurs de l'interface passent par ces variables (triplets HSL)
+ * afin que les modes clair et sombre restent cohérents.
+ */
:root {
- --background: 0 0% 98%;
- --foreground: 222 47% 11%;
-
+ --background: 210 40% 98%; /* #F8FAFC */
+ --foreground: 222 47% 11%; /* #0F172A */
+
--card: 0 0% 100%;
--card-foreground: 222 47% 11%;
-
+
--popover: 0 0% 100%;
--popover-foreground: 222 47% 11%;
-
- --primary: 215 85% 58%;
+
+ --primary: 243 75% 59%; /* #4F46E5 indigo */
--primary-foreground: 0 0% 100%;
-
- --secondary: 265 75% 62%;
- --secondary-foreground: 0 0% 100%;
-
- --muted: 210 40% 96%;
- --muted-foreground: 215 16% 47%;
-
- --accent: 210 40% 96%;
- --accent-foreground: 222 47% 11%;
-
- --destructive: 0 85% 62%;
+
+ --secondary: 220 14% 94%;
+ --secondary-foreground: 222 47% 11%;
+
+ --brand-accent: 6 93% 68%; /* #F97362 corail */
+ --brand-accent-foreground: 0 0% 100%;
+
+ --muted: 220 14% 95%;
+ --muted-foreground: 215 16% 42%;
+
+ --accent: 226 100% 97%;
+ --accent-foreground: 243 60% 40%;
+
+ --destructive: 0 72% 51%; /* #DC2626 */
--destructive-foreground: 0 0% 100%;
-
- --border: 214 32% 91%;
- --input: 214 32% 91%;
- --ring: 215 85% 58%;
-
- --success: 142 76% 48%;
- --warning: 35 95% 58%;
- --info: 195 85% 55%;
-
- --chart-1: 215 85% 58%;
- --chart-2: 265 75% 62%;
- --chart-3: 142 76% 48%;
- --chart-4: 195 85% 55%;
- --chart-5: 35 95% 58%;
-
+
+ --success: 142 72% 37%; /* #16A34A */
+ --success-foreground: 0 0% 100%;
+ --warning: 38 92% 50%; /* #F59E0B */
+ --warning-foreground: 26 90% 14%;
+ --info: 200 98% 39%; /* #0284C7 */
+ --info-foreground: 0 0% 100%;
+
+ --facebook: 214 89% 52%; /* #1877F2 */
+ --instagram: 345 75% 57%; /* #E4405F */
+ --tiktok: 0 0% 7%;
+
+ --border: 220 13% 90%;
+ --input: 220 13% 87%;
+ --ring: 243 75% 59%;
+
+ --chart-1: 243 75% 59%;
+ --chart-2: 6 93% 68%;
+ --chart-3: 142 72% 37%;
+ --chart-4: 200 98% 39%;
+ --chart-5: 38 92% 50%;
+
--sidebar: 0 0% 100%;
--sidebar-foreground: 222 47% 11%;
- --sidebar-primary: 215 85% 58%;
+ --sidebar-primary: 243 75% 59%;
--sidebar-primary-foreground: 0 0% 100%;
- --sidebar-accent: 210 40% 96%;
+ --sidebar-accent: 220 14% 96%;
--sidebar-accent-foreground: 222 47% 11%;
- --sidebar-border: 214 32% 91%;
- --sidebar-ring: 215 85% 58%;
-
+ --sidebar-border: 220 13% 91%;
+ --sidebar-ring: 243 75% 59%;
+
--radius: 0.75rem;
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ --font-serif: Georgia, 'Times New Roman', serif;
--font-mono: 'JetBrains Mono', 'Fira Code', Menlo, monospace;
}
.dark {
- --background: 222 20% 8%;
- --foreground: 0 0% 95%;
-
- --card: 222 16% 15%;
- --card-foreground: 0 0% 95%;
-
- --popover: 222 18% 12%;
- --popover-foreground: 0 0% 95%;
-
- --primary: 215 85% 58%;
- --primary-foreground: 0 0% 100%;
-
- --secondary: 265 75% 62%;
- --secondary-foreground: 0 0% 100%;
-
- --muted: 222 12% 20%;
- --muted-foreground: 222 10% 65%;
-
- --accent: 222 18% 12%;
- --accent-foreground: 0 0% 95%;
-
- --destructive: 0 85% 62%;
+ --background: 224 47% 7%;
+ --foreground: 210 40% 96%;
+
+ --card: 224 40% 11%;
+ --card-foreground: 210 40% 96%;
+
+ --popover: 224 40% 12%;
+ --popover-foreground: 210 40% 96%;
+
+ --primary: 239 84% 70%;
+ --primary-foreground: 224 47% 9%;
+
+ --secondary: 223 30% 18%;
+ --secondary-foreground: 210 40% 96%;
+
+ --brand-accent: 6 93% 70%;
+ --brand-accent-foreground: 224 47% 9%;
+
+ --muted: 223 30% 16%;
+ --muted-foreground: 217 20% 68%;
+
+ --accent: 236 40% 20%;
+ --accent-foreground: 230 90% 88%;
+
+ --destructive: 0 84% 63%;
--destructive-foreground: 0 0% 100%;
-
- --border: 222 12% 20%;
- --input: 222 12% 20%;
- --ring: 215 85% 58%;
-
- --success: 142 76% 48%;
- --warning: 35 95% 58%;
- --info: 195 85% 55%;
-
- --chart-1: 215 85% 58%;
- --chart-2: 265 75% 62%;
- --chart-3: 142 76% 48%;
- --chart-4: 195 85% 55%;
- --chart-5: 35 95% 58%;
-
- --sidebar: 222 20% 8%;
- --sidebar-foreground: 0 0% 95%;
- --sidebar-primary: 215 85% 58%;
- --sidebar-primary-foreground: 0 0% 100%;
- --sidebar-accent: 222 18% 12%;
- --sidebar-accent-foreground: 0 0% 95%;
- --sidebar-border: 222 12% 20%;
- --sidebar-ring: 215 85% 58%;
+
+ --success: 142 64% 50%;
+ --success-foreground: 144 60% 8%;
+ --warning: 43 96% 56%;
+ --warning-foreground: 26 90% 10%;
+ --info: 199 89% 60%;
+ --info-foreground: 204 80% 10%;
+
+ --facebook: 214 89% 62%;
+ --instagram: 345 80% 65%;
+ --tiktok: 0 0% 96%;
+
+ --border: 223 25% 20%;
+ --input: 223 25% 24%;
+ --ring: 239 84% 70%;
+
+ --chart-1: 239 84% 70%;
+ --chart-2: 6 93% 70%;
+ --chart-3: 142 64% 50%;
+ --chart-4: 199 89% 60%;
+ --chart-5: 43 96% 56%;
+
+ --sidebar: 224 45% 9%;
+ --sidebar-foreground: 210 40% 96%;
+ --sidebar-primary: 239 84% 70%;
+ --sidebar-primary-foreground: 224 47% 9%;
+ --sidebar-accent: 223 30% 15%;
+ --sidebar-accent-foreground: 210 40% 96%;
+ --sidebar-border: 223 25% 16%;
+ --sidebar-ring: 239 84% 70%;
}
@layer base {
@@ -109,50 +135,75 @@
@apply border-border;
}
+ html {
+ -webkit-tap-highlight-color: transparent;
+ }
+
body {
@apply font-sans antialiased bg-background text-foreground;
+ font-feature-settings: "cv02", "cv03", "cv04", "cv11";
}
-
+
h1, h2, h3, h4, h5, h6 {
- @apply font-semibold;
+ @apply font-semibold tracking-tight;
}
}
-/* Custom scrollbar */
+@layer utilities {
+ /* Dégradé de marque : réservé au logo et au bouton d'action principal. */
+ .gradient-brand {
+ background-image: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--brand-accent)));
+ }
+
+ /* Alias historique conservé pour les composants existants. */
+ .gradient-primary {
+ background-image: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--brand-accent)));
+ }
+
+ .gradient-text {
+ background-image: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--brand-accent)));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ }
+
+ .pb-safe {
+ padding-bottom: env(safe-area-inset-bottom);
+ }
+
+ .scrollbar-none {
+ scrollbar-width: none;
+ }
+ .scrollbar-none::-webkit-scrollbar {
+ display: none;
+ }
+}
+
+/* Barre de défilement discrète */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
- background: hsl(var(--muted) / 0.5);
- border-radius: 5px;
+ background: transparent;
}
::-webkit-scrollbar-thumb {
- background: linear-gradient(180deg, hsl(var(--primary)), hsl(var(--secondary)));
- border-radius: 5px;
+ background: hsl(var(--muted-foreground) / 0.25);
+ border-radius: 9999px;
border: 2px solid hsl(var(--background));
}
::-webkit-scrollbar-thumb:hover {
- background: linear-gradient(180deg, hsl(var(--primary) / 0.9), hsl(var(--secondary) / 0.9));
+ background: hsl(var(--muted-foreground) / 0.4);
}
-/* Smooth transitions */
-* {
- transition: background-color 200ms, border-color 200ms, color 200ms;
-}
-
-button, a {
- transition: all 200ms;
-}
-
-/* Animation for cards */
+/* Apparition douce des contenus */
@keyframes fadeIn {
from {
opacity: 0;
- transform: translateY(10px);
+ transform: translateY(6px);
}
to {
opacity: 1;
@@ -161,64 +212,23 @@ button, a {
}
.fade-in {
- animation: fadeIn 150ms ease-out;
+ animation: fadeIn 180ms ease-out;
}
-/* Gradient utilities */
-.gradient-primary {
- background: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--secondary)));
-}
-
-.gradient-text {
- background: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--secondary)));
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
-}
-
-/* Card hover effect */
+/* Survol des cartes cliquables */
.card-hover {
- transition: transform 200ms, box-shadow 200ms;
+ transition: transform 150ms, box-shadow 150ms, border-color 150ms;
}
.card-hover:hover {
- transform: translateY(-2px);
- box-shadow: 0 12px 24px hsl(var(--primary) / 0.12);
+ transform: translateY(-1px);
+ border-color: hsl(var(--primary) / 0.35);
+ box-shadow: 0 8px 20px -8px hsl(var(--primary) / 0.25);
}
-/* Platform badges */
-.badge-facebook {
- background: hsl(var(--primary) / 0.1);
- color: hsl(var(--primary));
- border: 1px solid hsl(var(--primary) / 0.25);
-}
-
-.badge-instagram {
- background: hsl(var(--secondary) / 0.1);
- color: hsl(var(--secondary));
- border: 1px solid hsl(var(--secondary) / 0.25);
-}
-
-/* Status indicators */
-.status-scheduled {
- color: hsl(var(--primary));
-}
-
-.status-published {
- color: hsl(var(--success));
-}
-
-.status-failed {
- color: hsl(var(--destructive));
-}
-
-.status-draft {
- color: hsl(var(--muted-foreground));
-}
-
-/* Skeleton loader */
+/* Squelette de chargement */
.skeleton {
- background: linear-gradient(90deg, hsl(var(--muted)) 25%, hsl(var(--accent)) 50%, hsl(var(--muted)) 75%);
+ background: linear-gradient(90deg, hsl(var(--muted)) 25%, hsl(var(--secondary)) 50%, hsl(var(--muted)) 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@@ -227,3 +237,10 @@ button, a {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
+
+@media (prefers-reduced-motion: reduce) {
+ .fade-in,
+ .skeleton {
+ animation: none;
+ }
+}
diff --git a/client/src/pages/AnalyticsPage.tsx b/client/src/pages/AnalyticsPage.tsx
deleted file mode 100644
index d5032ec..0000000
--- a/client/src/pages/AnalyticsPage.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import Sidebar from "@/components/sidebar";
-import TopBar from "@/components/topbar";
-import { AnalyticsDashboard } from '@/components/analytics/AnalyticsDashboard';
-import { useState } from "react";
-
-export default function AnalyticsPage() {
- const [sidebarOpen, setSidebarOpen] = useState(false);
-
- return (
-
- {sidebarOpen && (
-
setSidebarOpen(false)}
- />
- )}
-
-
- setSidebarOpen(false)} />
-
-
-
- setSidebarOpen(!sidebarOpen)} />
-
-
-
-
Analystique Sociale
-
Suivez vos performances Facebook & Instagram.
-
-
-
-
-
- );
-}
diff --git a/client/src/pages/ai.tsx b/client/src/pages/ai.tsx
index a3860cb..62fa3aa 100644
--- a/client/src/pages/ai.tsx
+++ b/client/src/pages/ai.tsx
@@ -1,41 +1,17 @@
-import { useState } from "react";
-import Sidebar from "@/components/sidebar";
-import TopBar from "@/components/topbar";
+import { Bot } from "lucide-react";
+import { Page } from "@/components/layout/app-shell";
+import { PageHeader } from "@/components/layout/page-header";
import AiChat from "@/components/ai-chat";
export default function AI() {
- const [sidebarOpen, setSidebarOpen] = useState(false);
-
return (
-
- {sidebarOpen && (
-
setSidebarOpen(false)}
- />
- )}
-
-
- setSidebarOpen(false)} />
-
-
-
- setSidebarOpen(!sidebarOpen)} />
-
-
-
-
Assistant IA
-
- Générez du contenu personnalisé avec l'intelligence artificielle
-
-
-
-
-
-
-
+
+
+
+
);
}
diff --git a/client/src/pages/analytics.tsx b/client/src/pages/analytics.tsx
new file mode 100644
index 0000000..1b18110
--- /dev/null
+++ b/client/src/pages/analytics.tsx
@@ -0,0 +1,17 @@
+import { BarChart3 } from "lucide-react";
+import { Page } from "@/components/layout/app-shell";
+import { PageHeader } from "@/components/layout/page-header";
+import { AnalyticsDashboard } from "@/components/analytics/AnalyticsDashboard";
+
+export default function AnalyticsPage() {
+ return (
+
+
+
+
+ );
+}
diff --git a/client/src/pages/calendar.tsx b/client/src/pages/calendar.tsx
index b6cb235..3528678 100644
--- a/client/src/pages/calendar.tsx
+++ b/client/src/pages/calendar.tsx
@@ -1,41 +1,26 @@
-import { useState } from "react";
-import Sidebar from "@/components/sidebar";
-import TopBar from "@/components/topbar";
+import { Link } from "wouter";
+import { Calendar as CalendarIcon, Plus } from "lucide-react";
+import { Page } from "@/components/layout/app-shell";
+import { PageHeader } from "@/components/layout/page-header";
+import { Button } from "@/components/ui/button";
import CalendarView from "@/components/calendar-view";
export default function Calendar() {
- const [sidebarOpen, setSidebarOpen] = useState(false);
-
return (
-
- {sidebarOpen && (
-
setSidebarOpen(false)}
- />
- )}
-
-
- setSidebarOpen(false)} />
-
-
-
- setSidebarOpen(!sidebarOpen)} />
-
-
-
-
Calendrier
-
- Visualisez et gérez vos publications planifiées
-
-
-
-
-
-
-
+
+
+
+ Programmer
+
+
+ }
+ />
+
+
);
}
diff --git a/client/src/pages/dashboard.tsx b/client/src/pages/dashboard.tsx
index a4de55c..1b18b9d 100644
--- a/client/src/pages/dashboard.tsx
+++ b/client/src/pages/dashboard.tsx
@@ -1,43 +1,56 @@
-import Sidebar from "@/components/sidebar";
-import TopBar from "@/components/topbar";
+import { Link } from "wouter";
+import { Film, PenSquare } from "lucide-react";
+import { Page } from "@/components/layout/app-shell";
+import { PageHeader } from "@/components/layout/page-header";
+import { Button } from "@/components/ui/button";
import StatsCards from "@/components/stats-cards";
import OngoingReels from "@/components/ongoing-reels";
import RecentPublications from "@/components/recent-publications";
import ManagedPages from "@/components/managed-pages";
-import { useState } from "react";
+import { useSession } from "@/hooks/use-session";
+
+function greeting(): string {
+ const h = new Date().getHours();
+ if (h < 5 || h >= 18) return "Bonsoir";
+ return "Bonjour";
+}
export default function Dashboard() {
- const [sidebarOpen, setSidebarOpen] = useState(false);
+ const { session } = useSession();
return (
-
- {sidebarOpen && (
-
setSidebarOpen(false)}
- />
- )}
+
+
+
+
+ >
+ }
+ />
-
- setSidebarOpen(false)} />
-
-
-
- setSidebarOpen(!sidebarOpen)} />
-
-
+
);
}
diff --git a/client/src/pages/login.tsx b/client/src/pages/login.tsx
index 98aac35..8f606e8 100644
--- a/client/src/pages/login.tsx
+++ b/client/src/pages/login.tsx
@@ -1,12 +1,12 @@
import { useState, useEffect } from "react";
import { useLocation } from "wouter";
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient";
-import { Lock, User, Sparkles } from "lucide-react";
+import { AlertTriangle, CheckCircle2, Loader2, Lock, User } from "lucide-react";
+import { Logo, LogoMark } from "@/components/brand/logo";
export default function Login() {
const [, setLocation] = useLocation();
@@ -56,38 +56,59 @@ export default function Login() {
};
return (
-
-
+
+ {/* Panneau de marque (desktop) */}
+
+
+
+
+
+ Social Flow
+
+
+
+ Vos réseaux sociaux, en pilote automatique.
+
+
+ {[
+ "Créez publications et Reels en quelques clics",
+ "Programmez sur Facebook, Instagram et TikTok",
+ "Laissez l'IA rédiger vos textes",
+ ].map((t) => (
+ -
+
+ {t}
+
+ ))}
+
+
+
© Social Flow
+
-
-
-
-
+ {/* Formulaire */}
+
+
+
+
-
-
- Social Flow
-
-
- Connectez-vous à votre compte pour gérer vos publications
-
-
-
-
-