mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
14 files changed
+82
-51
No files matched your search
@@ -22,6 +22,10 @@ externalPort = 3003
|
||||
localPort = 35357
|
||||
externalPort = 3002
|
||||
|
||||
[[ports]]
|
||||
localPort = 37431
|
||||
externalPort = 5000
|
||||
|
||||
[[ports]]
|
||||
localPort = 37659
|
||||
externalPort = 4200
|
||||
@@ -34,6 +38,10 @@ externalPort = 3000
|
||||
localPort = 43245
|
||||
externalPort = 3001
|
||||
|
||||
[[ports]]
|
||||
localPort = 46681
|
||||
externalPort = 5173
|
||||
|
||||
[env]
|
||||
PORT = "5000"
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@ import { Button } from "@/components/ui/button";
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export default function Sidebar() {
|
||||
interface SidebarProps {
|
||||
onLinkClick?: () => void;
|
||||
}
|
||||
|
||||
export default function Sidebar({ onLinkClick }: SidebarProps = {}) {
|
||||
const [location] = useLocation();
|
||||
const [, setLocation] = useLocation();
|
||||
const { toast } = useToast();
|
||||
@@ -27,6 +31,18 @@ export default function Sidebar() {
|
||||
|
||||
const isAdmin = session?.role === "admin";
|
||||
|
||||
const handleLinkClick = (href: string, e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
// Always close sidebar on mobile first
|
||||
if (onLinkClick) {
|
||||
onLinkClick();
|
||||
}
|
||||
// Navigate after a small delay to ensure state update completes
|
||||
setTimeout(() => {
|
||||
setLocation(href);
|
||||
}, 10);
|
||||
};
|
||||
|
||||
const logoutMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const res = await apiRequest("POST", "/api/auth/logout", {});
|
||||
@@ -82,10 +98,11 @@ export default function Sidebar() {
|
||||
|
||||
return (
|
||||
<li key={item.href} className="relative group">
|
||||
<Link
|
||||
<a
|
||||
href={item.href}
|
||||
onClick={(e) => handleLinkClick(item.href, e)}
|
||||
className={`
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl font-medium transition-all relative
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl font-medium transition-all relative cursor-pointer
|
||||
${isActive
|
||||
? 'bg-gradient-to-r from-primary/10 to-secondary/10 text-primary shadow-sm'
|
||||
: 'text-muted-foreground hover:bg-sidebar-accent hover:text-foreground'
|
||||
@@ -108,7 +125,7 @@ export default function Sidebar() {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
</a>
|
||||
{isCollapsed && (
|
||||
<div className="absolute left-full ml-2 px-3 py-2 bg-popover text-popover-foreground text-sm rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all whitespace-nowrap z-50">
|
||||
{item.label}
|
||||
@@ -131,10 +148,11 @@ export default function Sidebar() {
|
||||
|
||||
return (
|
||||
<li key={item.href} className="relative group">
|
||||
<Link
|
||||
<a
|
||||
href={item.href}
|
||||
onClick={(e) => handleLinkClick(item.href, e)}
|
||||
className={`
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative cursor-pointer
|
||||
${isActive
|
||||
? 'bg-gradient-to-r from-primary/10 to-secondary/10 text-primary shadow-sm'
|
||||
: 'text-muted-foreground hover:bg-sidebar-accent hover:text-foreground'
|
||||
@@ -148,7 +166,7 @@ export default function Sidebar() {
|
||||
)}
|
||||
<Icon className={`w-5 h-5 ${isActive ? 'text-primary' : ''}`} />
|
||||
{!isCollapsed && <span>{item.label}</span>}
|
||||
</Link>
|
||||
</a>
|
||||
{isCollapsed && (
|
||||
<div className="absolute left-full ml-2 px-3 py-2 bg-popover text-popover-foreground text-sm rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all whitespace-nowrap z-50">
|
||||
{item.label}
|
||||
@@ -170,10 +188,11 @@ export default function Sidebar() {
|
||||
<p className="text-xs font-semibold text-muted-foreground mb-2 px-4 uppercase tracking-wider">Administration</p>
|
||||
</div>
|
||||
)}
|
||||
<Link
|
||||
<a
|
||||
href="/pages"
|
||||
onClick={(e) => handleLinkClick("/pages", e)}
|
||||
className={`
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group cursor-pointer
|
||||
${location === "/pages"
|
||||
? 'bg-gradient-to-r from-primary/10 to-secondary/10 text-primary shadow-sm'
|
||||
: 'text-muted-foreground hover:bg-sidebar-accent hover:text-foreground'
|
||||
@@ -192,11 +211,12 @@ export default function Sidebar() {
|
||||
Pages gérées
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
</a>
|
||||
<a
|
||||
href="/users"
|
||||
onClick={(e) => handleLinkClick("/users", e)}
|
||||
className={`
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group cursor-pointer
|
||||
${location === "/users"
|
||||
? 'bg-gradient-to-r from-primary/10 to-secondary/10 text-primary shadow-sm'
|
||||
: 'text-muted-foreground hover:bg-sidebar-accent hover:text-foreground'
|
||||
@@ -215,11 +235,12 @@ export default function Sidebar() {
|
||||
Utilisateurs
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
</a>
|
||||
<a
|
||||
href="/sql"
|
||||
onClick={(e) => handleLinkClick("/sql", e)}
|
||||
className={`
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group cursor-pointer
|
||||
${location === "/sql"
|
||||
? 'bg-gradient-to-r from-primary/10 to-secondary/10 text-primary shadow-sm'
|
||||
: 'text-muted-foreground hover:bg-sidebar-accent hover:text-foreground'
|
||||
@@ -238,11 +259,12 @@ export default function Sidebar() {
|
||||
SQL
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
</a>
|
||||
<a
|
||||
href="/settings"
|
||||
onClick={(e) => handleLinkClick("/settings", e)}
|
||||
className={`
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group
|
||||
flex items-center gap-3 px-4 py-3 rounded-xl transition-all relative group cursor-pointer
|
||||
${location === "/settings"
|
||||
? 'bg-gradient-to-r from-primary/10 to-secondary/10 text-primary shadow-sm'
|
||||
: 'text-muted-foreground hover:bg-sidebar-accent hover:text-foreground'
|
||||
@@ -261,7 +283,7 @@ export default function Sidebar() {
|
||||
Paramètres
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -302,10 +324,10 @@ export default function Sidebar() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Toggle Button */}
|
||||
{/* Toggle Button (Desktop only) */}
|
||||
<button
|
||||
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||
className="mt-2 w-full flex items-center justify-center p-2 text-muted-foreground hover:text-primary hover:bg-sidebar-accent rounded-xl transition-all"
|
||||
className="mt-2 w-full hidden lg:flex items-center justify-center p-2 text-muted-foreground hover:text-primary hover:bg-primary/10 border-2 border-border hover:border-primary/50 rounded-xl transition-all"
|
||||
data-testid="button-toggle-sidebar"
|
||||
>
|
||||
{isCollapsed ? <ChevronRight className="w-5 h-5" /> : <ChevronLeft className="w-5 h-5" />}
|
||||
|
||||
@@ -8,12 +8,13 @@ interface TopBarProps {
|
||||
export default function TopBar({ onMenuClick }: TopBarProps) {
|
||||
return (
|
||||
<header className="bg-card/50 backdrop-blur-xl border-b border-border/50 sticky top-0 z-10">
|
||||
<div className="flex items-center justify-between px-6 lg:px-8 py-5">
|
||||
<div className="flex items-center gap-6 flex-1">
|
||||
<div className="flex items-center justify-between px-4 sm:px-6 lg:px-8 py-4 sm:py-5">
|
||||
<div className="flex items-center gap-3 sm:gap-6 flex-1">
|
||||
<button
|
||||
className="lg:hidden text-foreground hover:text-primary transition-colors"
|
||||
className="lg:hidden text-foreground hover:text-primary transition-all hover:scale-110 active:scale-95 p-2 hover:bg-primary/10 rounded-xl"
|
||||
onClick={onMenuClick}
|
||||
data-testid="button-menu"
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
<Menu className="w-6 h-6" />
|
||||
</button>
|
||||
|
||||
@@ -20,10 +20,10 @@ const buttonVariants = cva(
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
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",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -19,13 +19,13 @@ export default function AI() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-5xl mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-5xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Assistant IA</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
|
||||
@@ -19,13 +19,13 @@ export default function Calendar() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-[1600px] mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-[1600px] mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Calendrier</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
|
||||
@@ -21,13 +21,13 @@ export default function Dashboard() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 space-y-8 max-w-[1600px] mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 space-y-6 sm:space-y-8 max-w-[1600px] mx-auto">
|
||||
<StatsCards />
|
||||
|
||||
<RecentPublications />
|
||||
|
||||
@@ -31,13 +31,13 @@ export default function History() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-5xl mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-5xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Historique</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
|
||||
@@ -19,13 +19,13 @@ export default function Media() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-[1600px] mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-[1600px] mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Médiathèque</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
|
||||
@@ -202,13 +202,13 @@ export default function NewPost() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-6xl mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-6xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Nouvelle publication</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
@@ -216,7 +216,7 @@ export default function NewPost() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6 lg:gap-8">
|
||||
<div className="space-y-8">
|
||||
<Card className="rounded-2xl border-border/50 shadow-lg">
|
||||
<CardHeader className="p-6">
|
||||
|
||||
@@ -55,13 +55,13 @@ export default function PagesManagement() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-[1600px] mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-[1600px] mx-auto">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">Pages gérées</h1>
|
||||
|
||||
@@ -132,13 +132,13 @@ export default function Settings() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-4xl mx-auto">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-4xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Paramètres</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
|
||||
@@ -87,13 +87,13 @@ export default function SqlAdmin() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-[1600px] mx-auto space-y-8">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-[1600px] mx-auto space-y-6 sm:space-y-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold flex items-center gap-2">
|
||||
<Database className="w-8 h-8" />
|
||||
@@ -104,7 +104,7 @@ export default function SqlAdmin() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4 sm:gap-6 lg:gap-8">
|
||||
{/* Liste des tables */}
|
||||
<Card className="rounded-2xl border-border/50 shadow-lg">
|
||||
<CardHeader className="p-6">
|
||||
|
||||
@@ -290,13 +290,13 @@ export default function UsersAdmin() {
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}>
|
||||
<Sidebar />
|
||||
<Sidebar onLinkClick={() => setSidebarOpen(false)} />
|
||||
</div>
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
<div className="p-8 max-w-[1600px] mx-auto space-y-8">
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-[1600px] mx-auto space-y-6 sm:space-y-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold flex items-center gap-2">
|
||||
<Users className="w-8 h-8" />
|
||||
@@ -307,7 +307,7 @@ export default function UsersAdmin() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6 lg:gap-8">
|
||||
{/* Liste des utilisateurs */}
|
||||
<div className="lg:col-span-2">
|
||||
<Card className="rounded-2xl border-border/50 shadow-lg">
|
||||
|
||||
Reference in new issue
Block a user