Introduce a mobile-friendly phone mode with a bottom navigation bar

Add phone mode functionality with a new bottom navigation component and adjust layout styles for smaller screens. Includes logic for auto-detection and user preference saving for phone mode. Also, redirects authenticated users from `/auth` to the dashboard.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 95e8aab4-f39f-4b48-aa57-a759ec15ba3d
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/95e8aab4-f39f-4b48-aa57-a759ec15ba3d/oL3c3As
This commit is contained in:
michaelschal committed 2025-09-11 08:19:18 +00:00
1 parent 6f5b5cb054
commit 9930f40105
6 files changed
+397 -16

No files matched your search

+65 -14
View File
@@ -1,13 +1,16 @@
import { ReactNode, useState, useEffect } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useLocation } from "wouter";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Button } from "@/components/ui/button";
import { LogOut, Store, Menu, AlertTriangle } from "lucide-react";
import { LogOut, Store, Menu, AlertTriangle, Smartphone, Monitor } from "lucide-react";
import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useIsMobile } from "@/hooks/use-mobile";
import { useScreenSize } from "@/hooks/use-screen-size";
import { usePhoneMode } from "@/hooks/use-phone-mode";
import { StoreProvider } from "@/contexts/StoreContext";
import Sidebar from "./Sidebar";
import PhoneBottomNav from "./PhoneBottomNav";
import WeatherWidget from "./WeatherWidget";
import DateWidget from "./DateWidget";
import type { Group } from "@shared/schema";
@@ -19,9 +22,11 @@ interface LayoutProps {
export default function Layout({ children }: LayoutProps) {
const { user } = useAuthUnified();
const [location] = useLocation();
const queryClient = useQueryClient();
const isMobile = useIsMobile();
const { screenSize, isMobileOrTablet, isTablet } = useScreenSize();
const { isPhoneMode, setPhoneMode, canUsePhoneMode } = usePhoneMode();
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
// Restaurer le selectedStoreId depuis localStorage si disponible
@@ -120,28 +125,35 @@ export default function Layout({ children }: LayoutProps) {
return (
<StoreProvider value={{ selectedStoreId, setSelectedStoreId, stores, sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen, storeInitialized }}>
<div className="layout-container flex bg-gray-50">
{/* Mobile overlay */}
{isMobile && mobileMenuOpen && (
<div className={`layout-container flex bg-gray-50 ${isPhoneMode ? 'phone-mode' : ''}`}>
{/* Mobile overlay for normal mode */}
{!isPhoneMode && isMobile && mobileMenuOpen && (
<div
className="fixed inset-0 bg-black bg-opacity-50 z-40"
onClick={() => setMobileMenuOpen(false)}
/>
)}
<Sidebar />
{/* Sidebar for normal mode */}
{!isPhoneMode && <Sidebar />}
<main className={`flex-1 flex flex-col h-full ${
// Marge gauche uniquement si la sidebar est en position fixed (tablet expanded)
isMobile ? 'ml-0' : (isTablet && !sidebarCollapsed) ? 'ml-48' : 'ml-0'
// Ajustement des marges selon le mode
isPhoneMode
? 'ml-0 mb-20' // Mode téléphone : pas de marge gauche, marge bas pour bottom nav
: isMobile
? 'ml-0'
: (isTablet && !sidebarCollapsed) ? 'ml-48' : 'ml-0'
}`}>
{/* Header with mobile menu and store selector */}
<header className={`h-16 bg-white border-b border-gray-200 flex items-center justify-between ${
<header className={`${
isPhoneMode ? 'h-12' : 'h-16'
} bg-white border-b border-gray-200 flex items-center justify-between ${
isMobileOrTablet ? 'px-3' : 'px-6'
}`}>
<div className="flex items-center gap-2 sm:gap-4 min-w-0 flex-1">
{/* Mobile menu button */}
{isMobile && (
{/* Mobile menu button (only in normal mode) */}
{!isPhoneMode && isMobile && (
<Button
variant="ghost"
size="sm"
@@ -157,13 +169,47 @@ export default function Layout({ children }: LayoutProps) {
<div className={`flex items-center ${
isMobile ? 'gap-1' : 'gap-3'
} min-w-0`}>
<WeatherWidget />
{!isMobile && <DateWidget />}
{/* En mode téléphone, affichage simplifié */}
{isPhoneMode ? (
<div className="flex items-center gap-2">
<WeatherWidget />
{/* Phone mode toggle button */}
<Button
variant="ghost"
size="sm"
onClick={() => setPhoneMode(!isPhoneMode)}
className="min-h-[44px] min-w-[44px] p-2 flex-shrink-0"
data-testid="button-phone-mode-toggle"
title="Basculer vers le mode normal"
>
<Monitor className="h-4 w-4 text-blue-600" />
</Button>
</div>
) : (
<>
<WeatherWidget />
{!isMobile && <DateWidget />}
{/* Phone mode toggle button */}
{canUsePhoneMode && (
<Button
variant="ghost"
size="sm"
onClick={() => setPhoneMode(!isPhoneMode)}
className="min-h-[44px] min-w-[44px] p-2 flex-shrink-0"
data-testid="button-phone-mode-toggle"
title="Basculer vers le mode téléphone"
>
<Smartphone className="h-4 w-4 text-gray-600" />
</Button>
)}
</>
)}
</div>
</div>
{/* Store selector for admin, directeur, and manager - responsive */}
{user && (user.role === 'admin' || user.role === 'directeur' || user.role === 'manager') && stores.length > 0 && (
{!isPhoneMode && user && (user.role === 'admin' || user.role === 'directeur' || user.role === 'manager') && stores.length > 0 && (
<div className="flex items-center gap-1 sm:gap-2 flex-shrink-0">
<Store className="h-4 w-4 text-gray-500 hidden sm:block" />
<Select
@@ -253,11 +299,16 @@ export default function Layout({ children }: LayoutProps) {
)}
<div className={`flex-1 bg-gray-50 h-full overflow-y-auto ${
isMobileOrTablet ? 'p-3' : 'p-6'
isPhoneMode
? 'p-2' // Mode téléphone : padding réduit
: isMobileOrTablet ? 'p-3' : 'p-6'
}`}>
{children}
</div>
</main>
{/* Bottom navigation for phone mode (only when authenticated) */}
{isPhoneMode && user && <PhoneBottomNav user={user} location={location} />}
</div>
</StoreProvider>
);