mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-22 22:48:15 +02:00
feat: add mobile layout and bottom navigation components
This commit is contained in:
1 parent
69773e3d2f
commit
946b5f30bd
12 files changed
+1219
-4
No files matched your search
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* MobileLayout - Layout dédié pour l'application mobile
|
||||
* Affiche le magasin sélectionné de manière visible et une navigation bottom
|
||||
*/
|
||||
import { ReactNode, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Menu, X, Store, ChevronDown, LogOut } from "lucide-react";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import MobileBottomNav from "./MobileBottomNav";
|
||||
import type { Group } from "@shared/schema";
|
||||
|
||||
interface MobileLayoutProps {
|
||||
children: ReactNode;
|
||||
title?: string;
|
||||
showBackButton?: boolean;
|
||||
onBack?: () => void;
|
||||
}
|
||||
|
||||
export default function MobileLayout({ children, title }: MobileLayoutProps) {
|
||||
const { user } = useAuthUnified();
|
||||
const { selectedStoreId, setSelectedStoreId, stores } = useStore();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
// Get selected store name
|
||||
const selectedStore = stores?.find((s: Group) => s.id === selectedStoreId);
|
||||
const storeName = selectedStore?.name || "Tous les magasins";
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await fetch('/api/logout', { method: 'POST', credentials: 'include' });
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
} finally {
|
||||
window.location.href = "/auth";
|
||||
}
|
||||
};
|
||||
|
||||
const handleStoreChange = (value: string) => {
|
||||
const newStoreId = value === "all" ? null : parseInt(value);
|
||||
if (newStoreId) {
|
||||
localStorage.setItem('selectedStoreId', newStoreId.toString());
|
||||
} else {
|
||||
localStorage.removeItem('selectedStoreId');
|
||||
}
|
||||
setSelectedStoreId(newStoreId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="min-h-screen bg-gray-50 flex flex-col"
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: '100vw',
|
||||
overflowX: 'hidden',
|
||||
paddingBottom: '80px' // Space for bottom nav
|
||||
}}
|
||||
>
|
||||
{/* Header fixe */}
|
||||
<header className="bg-white border-b border-gray-200 sticky top-0 z-50">
|
||||
<div className="flex items-center justify-between h-14 px-3">
|
||||
{/* Menu hamburger */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setMenuOpen(true)}
|
||||
className="h-10 w-10 p-0"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
</Button>
|
||||
|
||||
{/* Logo */}
|
||||
<div className="flex items-center gap-1">
|
||||
<Store className="h-5 w-5 text-blue-600" />
|
||||
<span className="font-semibold text-gray-900">LogiFlow</span>
|
||||
</div>
|
||||
|
||||
{/* Magasin sélectionné - bien visible */}
|
||||
<div
|
||||
className="flex items-center gap-1 bg-blue-50 px-2 py-1 rounded-lg cursor-pointer"
|
||||
onClick={() => setMenuOpen(true)}
|
||||
>
|
||||
<span className="text-xs font-medium text-blue-700 truncate max-w-[80px]">
|
||||
{storeName}
|
||||
</span>
|
||||
<ChevronDown className="h-3 w-3 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Titre de page optionnel */}
|
||||
{title && (
|
||||
<div className="px-3 pb-2">
|
||||
<h1 className="text-lg font-bold text-gray-900">{title}</h1>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{/* Contenu principal */}
|
||||
<main className="flex-1 overflow-y-auto overflow-x-hidden">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* Navigation bottom */}
|
||||
<MobileBottomNav />
|
||||
|
||||
{/* Menu latéral (Sheet) */}
|
||||
<Sheet open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
<SheetContent side="left" className="w-[280px] p-0">
|
||||
<SheetHeader className="p-4 border-b">
|
||||
<SheetTitle className="flex items-center gap-2">
|
||||
<Store className="h-5 w-5 text-blue-600" />
|
||||
LogiFlow
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="p-4 space-y-6">
|
||||
{/* Utilisateur */}
|
||||
{user && (
|
||||
<div className="bg-gray-50 rounded-lg p-3">
|
||||
<p className="font-medium text-gray-900">
|
||||
{user.firstName} {user.lastName}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 capitalize">{user.role}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sélecteur de magasin */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Magasin
|
||||
</label>
|
||||
<Select
|
||||
value={selectedStoreId?.toString() || (user?.role === 'admin' ? "all" : "")}
|
||||
onValueChange={handleStoreChange}
|
||||
>
|
||||
<SelectTrigger className="w-full h-12">
|
||||
<SelectValue placeholder="Sélectionner un magasin" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{user?.role === 'admin' && (
|
||||
<SelectItem value="all">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 bg-gray-400 rounded-full" />
|
||||
Tous les magasins
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
{stores?.map((store: Group) => (
|
||||
<SelectItem key={store.id} value={store.id.toString()}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-3 h-3 rounded-full"
|
||||
style={{ backgroundColor: store.color || '#gray' }}
|
||||
/>
|
||||
{store.name}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Déconnexion */}
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full h-12 text-red-600 border-red-200 hover:bg-red-50"
|
||||
onClick={handleLogout}
|
||||
>
|
||||
<LogOut className="h-4 w-4 mr-2" />
|
||||
Déconnexion
|
||||
</Button>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user