Adapt the application interface for mobile and tablet viewing

Enhance responsiveness of the application's layout and various pages (Dashboard, Deliveries, Orders) to better support mobile and tablet screen sizes by implementing conditional rendering for navigation, adjusting padding, and optimizing element display.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 76582ba3-44cb-4730-92fa-66f28725967b
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/76582ba3-44cb-4730-92fa-66f28725967b/GDasGiN
This commit is contained in:
michaelschal committed 2025-08-12 20:55:50 +00:00
1 parent a7936fab8d
commit 597d3564ee
5 files changed
+244 -134

No files matched your search

+37 -10
View File
@@ -2,8 +2,9 @@ import { ReactNode, useState, createContext, useContext } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Button } from "@/components/ui/button";
import { LogOut, Store } from "lucide-react";
import { LogOut, Store, Menu } from "lucide-react";
import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useIsMobile } from "@/hooks/use-mobile";
import Sidebar from "./Sidebar";
import type { Group } from "@shared/schema";
@@ -13,6 +14,8 @@ interface StoreContextType {
stores: Group[];
sidebarCollapsed: boolean;
setSidebarCollapsed: (collapsed: boolean) => void;
mobileMenuOpen: boolean;
setMobileMenuOpen: (open: boolean) => void;
}
const StoreContext = createContext<StoreContextType | undefined>(undefined);
@@ -32,6 +35,8 @@ interface LayoutProps {
export default function Layout({ children }: LayoutProps) {
const { user } = useAuthUnified();
const queryClient = useQueryClient();
const isMobile = useIsMobile();
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
// Restaurer le selectedStoreId depuis localStorage si disponible
const saved = localStorage.getItem('selectedStoreId');
@@ -41,11 +46,13 @@ export default function Layout({ children }: LayoutProps) {
});
const [sidebarCollapsed, setSidebarCollapsed] = useState<boolean>(() => {
// Restaurer l'état de la sidebar depuis localStorage
// Restaurer l'état de la sidebar depuis localStorage, mais forcer collapsed sur mobile
const saved = localStorage.getItem('sidebarCollapsed');
return saved ? JSON.parse(saved) : false;
return isMobile ? true : (saved ? JSON.parse(saved) : false);
});
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const { data: stores = [] } = useQuery<Group[]>({
queryKey: ['/api/groups'],
enabled: !!user,
@@ -56,20 +63,40 @@ export default function Layout({ children }: LayoutProps) {
};
return (
<StoreContext.Provider value={{ selectedStoreId, setSelectedStoreId, stores, sidebarCollapsed, setSidebarCollapsed }}>
<StoreContext.Provider value={{ selectedStoreId, setSelectedStoreId, stores, sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen }}>
<div className="flex h-screen overflow-hidden bg-gray-50">
{/* Mobile overlay */}
{isMobile && mobileMenuOpen && (
<div
className="fixed inset-0 bg-black bg-opacity-50 z-40"
onClick={() => setMobileMenuOpen(false)}
/>
)}
<Sidebar />
<main className="flex-1 flex flex-col overflow-hidden">
{/* Header with store selector for admin */}
<header className="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6">
{/* Header with mobile menu and store selector */}
<header className="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-4 sm:px-6">
<div className="flex items-center gap-4">
{/* Mobile menu button */}
{isMobile && (
<Button
variant="ghost"
size="sm"
onClick={() => setMobileMenuOpen(true)}
className="h-8 w-8 p-0"
>
<Menu className="h-5 w-5" />
</Button>
)}
<h1 className="text-lg font-medium text-gray-800">LogiFlow</h1>
</div>
{/* Store selector for admin - moved to top right */}
{/* Store selector for admin - responsive */}
{user?.role === 'admin' && stores.length > 0 && (
<div className="flex items-center gap-2">
<Store className="h-4 w-4 text-gray-500" />
<Store className="h-4 w-4 text-gray-500 hidden sm:block" />
<Select
value={selectedStoreId?.toString() || "all"}
onValueChange={(value) => {
@@ -96,8 +123,8 @@ export default function Layout({ children }: LayoutProps) {
setSelectedStoreId(newStoreId);
}}
>
<SelectTrigger className="w-64 border border-gray-300">
<SelectValue placeholder="Sélectionner un magasin" />
<SelectTrigger className="w-32 sm:w-64 border border-gray-300">
<SelectValue placeholder="Magasin" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">
+149 -73
View File
@@ -2,6 +2,7 @@ import { Link, useLocation } from "wouter";
import { useAuthSimple } from "@/hooks/useAuthSimple";
import { Button } from "@/components/ui/button";
import { useStore } from "./Layout";
import { useIsMobile } from "@/hooks/use-mobile";
import {
Store,
Calendar,
@@ -19,13 +20,15 @@ import {
Clock,
ListTodo,
ChevronLeft,
ChevronRight
ChevronRight,
X
} from "lucide-react";
export default function Sidebar() {
const { user, isLoading, error } = useAuthSimple();
const [location] = useLocation();
const { sidebarCollapsed, setSidebarCollapsed } = useStore();
const { sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen } = useStore();
const isMobile = useIsMobile();
// Debug logging pour production
console.log('Sidebar - User:', user);
@@ -168,34 +171,61 @@ export default function Sidebar() {
// Classes responsives pour la sidebar
const sidebarClasses = isMobile
? `fixed inset-y-0 left-0 z-50 w-64 bg-white border-r border-gray-200 shadow-lg transform transition-transform duration-300 ease-in-out ${
mobileMenuOpen ? 'translate-x-0' : '-translate-x-full'
}`
: `${sidebarCollapsed ? 'w-16' : 'w-64'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`;
// Si l'utilisateur n'est pas encore chargé, afficher un état de chargement
if (isLoading) {
return (
<aside className={`${sidebarCollapsed ? 'w-16' : 'w-64'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
<aside className={sidebarClasses}>
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
{/* Mobile close button */}
{isMobile ? (
<div className="flex items-center justify-between w-full">
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => setMobileMenuOpen(false)}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
<X className="h-4 w-4" />
</Button>
</div>
) : (
<>
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
</div>
)}
{sidebarCollapsed && (
<div className="flex items-center justify-center w-full">
<Store className="h-6 w-6 text-blue-600" />
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
{sidebarCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</>
)}
{sidebarCollapsed && (
<div className="flex items-center justify-center w-full">
<Store className="h-6 w-6 text-blue-600" />
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
{sidebarCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</div>
<div className="flex-1 flex items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
@@ -207,31 +237,51 @@ export default function Sidebar() {
// Si l'utilisateur n'est pas authentifié, afficher seulement le logo
if (!user) {
return (
<aside className={`${sidebarCollapsed ? 'w-16' : 'w-64'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
<aside className={sidebarClasses}>
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
{/* Mobile close button */}
{isMobile ? (
<div className="flex items-center justify-between w-full">
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => setMobileMenuOpen(false)}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
<X className="h-4 w-4" />
</Button>
</div>
) : (
<>
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
</div>
)}
{sidebarCollapsed && (
<div className="flex items-center justify-center w-full">
<Store className="h-6 w-6 text-blue-600" />
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
{sidebarCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</>
)}
{sidebarCollapsed && (
<div className="flex items-center justify-center w-full">
<Store className="h-6 w-6 text-blue-600" />
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
{sidebarCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</div>
<div className="flex-1 flex items-center justify-center">
<div className="text-center text-gray-500">
@@ -242,33 +292,59 @@ export default function Sidebar() {
);
}
const handleMenuClick = () => {
if (isMobile) {
setMobileMenuOpen(false);
}
};
return (
<aside className={`${sidebarCollapsed ? 'w-16' : 'w-64'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
<aside className={sidebarClasses}>
{/* Logo et bouton de collapse */}
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
{/* Mobile close button */}
{isMobile ? (
<div className="flex items-center justify-between w-full">
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => setMobileMenuOpen(false)}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
<X className="h-4 w-4" />
</Button>
</div>
) : (
<>
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
</div>
)}
{sidebarCollapsed && (
<div className="flex items-center justify-center w-full">
<Store className="h-6 w-6 text-blue-600" />
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
{sidebarCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</>
)}
{sidebarCollapsed && (
<div className="flex items-center justify-center w-full">
<Store className="h-6 w-6 text-blue-600" />
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
{sidebarCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</div>
{/* Navigation */}
@@ -290,16 +366,16 @@ export default function Sidebar() {
return (
<Link key={item.path} href={item.path}>
<div
className={`flex items-center ${sidebarCollapsed ? 'px-3 py-3 justify-center' : 'px-3 py-2'} text-sm font-medium transition-colors hover:bg-gray-100 ${
className={`flex items-center ${sidebarCollapsed && !isMobile ? 'px-3 py-3 justify-center' : 'px-3 py-2'} text-sm font-medium transition-colors hover:bg-gray-100 ${
active
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700'
: 'text-gray-700'
}`}
title={sidebarCollapsed ? item.label : undefined}
onClick={() => console.log(`Navigating to: ${item.path}`)}
title={sidebarCollapsed && !isMobile ? item.label : undefined}
onClick={handleMenuClick}
>
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
{!sidebarCollapsed && item.label}
<Icon className={`h-4 w-4 ${sidebarCollapsed && !isMobile ? '' : 'mr-3'}`} />
{(!sidebarCollapsed || isMobile) && item.label}
</div>
</Link>
);