mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-21 22:18:17 +02:00
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:
1 parent
6f5b5cb054
commit
9930f40105
6 files changed
+397
-16
No files matched your search
@@ -16,8 +16,8 @@ localPort = 5000
|
|||||||
externalPort = 80
|
externalPort = 80
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 45995
|
localPort = 41343
|
||||||
externalPort = 3000
|
externalPort = 3001
|
||||||
|
|
||||||
[workflows]
|
[workflows]
|
||||||
runButton = "Project"
|
runButton = "Project"
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import { ReactNode, useState, useEffect } from "react";
|
import { ReactNode, useState, useEffect } from "react";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { useLocation } from "wouter";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Button } from "@/components/ui/button";
|
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 { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
import { useScreenSize } from "@/hooks/use-screen-size";
|
import { useScreenSize } from "@/hooks/use-screen-size";
|
||||||
|
import { usePhoneMode } from "@/hooks/use-phone-mode";
|
||||||
import { StoreProvider } from "@/contexts/StoreContext";
|
import { StoreProvider } from "@/contexts/StoreContext";
|
||||||
import Sidebar from "./Sidebar";
|
import Sidebar from "./Sidebar";
|
||||||
|
import PhoneBottomNav from "./PhoneBottomNav";
|
||||||
import WeatherWidget from "./WeatherWidget";
|
import WeatherWidget from "./WeatherWidget";
|
||||||
import DateWidget from "./DateWidget";
|
import DateWidget from "./DateWidget";
|
||||||
import type { Group } from "@shared/schema";
|
import type { Group } from "@shared/schema";
|
||||||
@@ -19,9 +22,11 @@ interface LayoutProps {
|
|||||||
|
|
||||||
export default function Layout({ children }: LayoutProps) {
|
export default function Layout({ children }: LayoutProps) {
|
||||||
const { user } = useAuthUnified();
|
const { user } = useAuthUnified();
|
||||||
|
const [location] = useLocation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const { screenSize, isMobileOrTablet, isTablet } = useScreenSize();
|
const { screenSize, isMobileOrTablet, isTablet } = useScreenSize();
|
||||||
|
const { isPhoneMode, setPhoneMode, canUsePhoneMode } = usePhoneMode();
|
||||||
|
|
||||||
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
|
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
|
||||||
// Restaurer le selectedStoreId depuis localStorage si disponible
|
// Restaurer le selectedStoreId depuis localStorage si disponible
|
||||||
@@ -120,28 +125,35 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StoreProvider value={{ selectedStoreId, setSelectedStoreId, stores, sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen, storeInitialized }}>
|
<StoreProvider value={{ selectedStoreId, setSelectedStoreId, stores, sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen, storeInitialized }}>
|
||||||
<div className="layout-container flex bg-gray-50">
|
<div className={`layout-container flex bg-gray-50 ${isPhoneMode ? 'phone-mode' : ''}`}>
|
||||||
{/* Mobile overlay */}
|
{/* Mobile overlay for normal mode */}
|
||||||
{isMobile && mobileMenuOpen && (
|
{!isPhoneMode && isMobile && mobileMenuOpen && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 bg-black bg-opacity-50 z-40"
|
className="fixed inset-0 bg-black bg-opacity-50 z-40"
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Sidebar />
|
{/* Sidebar for normal mode */}
|
||||||
|
{!isPhoneMode && <Sidebar />}
|
||||||
|
|
||||||
<main className={`flex-1 flex flex-col h-full ${
|
<main className={`flex-1 flex flex-col h-full ${
|
||||||
// Marge gauche uniquement si la sidebar est en position fixed (tablet expanded)
|
// Ajustement des marges selon le mode
|
||||||
isMobile ? 'ml-0' : (isTablet && !sidebarCollapsed) ? 'ml-48' : 'ml-0'
|
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 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'
|
isMobileOrTablet ? 'px-3' : 'px-6'
|
||||||
}`}>
|
}`}>
|
||||||
<div className="flex items-center gap-2 sm:gap-4 min-w-0 flex-1">
|
<div className="flex items-center gap-2 sm:gap-4 min-w-0 flex-1">
|
||||||
{/* Mobile menu button */}
|
{/* Mobile menu button (only in normal mode) */}
|
||||||
{isMobile && (
|
{!isPhoneMode && isMobile && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -157,13 +169,47 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
<div className={`flex items-center ${
|
<div className={`flex items-center ${
|
||||||
isMobile ? 'gap-1' : 'gap-3'
|
isMobile ? 'gap-1' : 'gap-3'
|
||||||
} min-w-0`}>
|
} min-w-0`}>
|
||||||
<WeatherWidget />
|
{/* En mode téléphone, affichage simplifié */}
|
||||||
{!isMobile && <DateWidget />}
|
{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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Store selector for admin, directeur, and manager - responsive */}
|
{/* 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">
|
<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" />
|
<Store className="h-4 w-4 text-gray-500 hidden sm:block" />
|
||||||
<Select
|
<Select
|
||||||
@@ -253,11 +299,16 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={`flex-1 bg-gray-50 h-full overflow-y-auto ${
|
<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}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
{/* Bottom navigation for phone mode (only when authenticated) */}
|
||||||
|
{isPhoneMode && user && <PhoneBottomNav user={user} location={location} />}
|
||||||
</div>
|
</div>
|
||||||
</StoreProvider>
|
</StoreProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { Home, Package, Truck, ClipboardList, MoreHorizontal } from "lucide-react"
|
||||||
|
import { Link, useLocation } from "wouter"
|
||||||
|
import { useState } from "react"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import {
|
||||||
|
Sheet,
|
||||||
|
SheetContent,
|
||||||
|
SheetDescription,
|
||||||
|
SheetHeader,
|
||||||
|
SheetTitle,
|
||||||
|
SheetTrigger,
|
||||||
|
} from "@/components/ui/sheet"
|
||||||
|
import type { User } from "@shared/schema"
|
||||||
|
|
||||||
|
interface NavItem {
|
||||||
|
path: string
|
||||||
|
label: string
|
||||||
|
icon: any
|
||||||
|
primary?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MoreMenuItem {
|
||||||
|
path: string
|
||||||
|
label: string
|
||||||
|
icon: any
|
||||||
|
roles?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryNavItems: NavItem[] = [
|
||||||
|
{ path: "/", label: "Accueil", icon: Home, primary: true },
|
||||||
|
{ path: "/orders", label: "Commandes", icon: Package, primary: true },
|
||||||
|
{ path: "/deliveries", label: "Livraisons", icon: Truck, primary: true },
|
||||||
|
{ path: "/tasks", label: "Tâches", icon: ClipboardList, primary: true },
|
||||||
|
]
|
||||||
|
|
||||||
|
const moreMenuItems: MoreMenuItem[] = [
|
||||||
|
{ path: "/bl-reconciliation", label: "Rapprochement", icon: ClipboardList, roles: ["admin", "directeur", "manager"] },
|
||||||
|
{ path: "/publicities", label: "Publicités", icon: Package, roles: ["admin", "directeur", "manager"] },
|
||||||
|
{ path: "/customer-orders", label: "Commandes Clients", icon: Package, roles: ["admin", "directeur", "manager"] },
|
||||||
|
{ path: "/avoirs", label: "Avoirs", icon: Package, roles: ["admin", "directeur", "manager"] },
|
||||||
|
{ path: "/dlc", label: "DLC", icon: Package, roles: ["admin", "directeur", "manager"] },
|
||||||
|
{ path: "/sav", label: "SAV", icon: ClipboardList, roles: ["admin", "directeur", "manager"] },
|
||||||
|
{ path: "/calendar", label: "Calendrier", icon: ClipboardList },
|
||||||
|
]
|
||||||
|
|
||||||
|
interface PhoneBottomNavProps {
|
||||||
|
user: User
|
||||||
|
location: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PhoneBottomNav({ user, location }: PhoneBottomNavProps) {
|
||||||
|
const [moreSheetOpen, setMoreSheetOpen] = useState(false)
|
||||||
|
|
||||||
|
const isActive = (path: string) => {
|
||||||
|
if (path === "/") return location === "/"
|
||||||
|
return location.startsWith(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPermission = (roles?: string[]) => {
|
||||||
|
if (!roles || !user) return true
|
||||||
|
return roles.includes(user.role)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredMoreItems = moreMenuItems.filter(item => hasPermission(item.roles))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 z-50 safe-area-pb bottom-nav">
|
||||||
|
<div className="flex items-center justify-around py-2 px-1">
|
||||||
|
{/* Navigation principale */}
|
||||||
|
{primaryNavItems.map((item) => {
|
||||||
|
const Icon = item.icon
|
||||||
|
const active = isActive(item.path)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link key={item.path} href={item.path}>
|
||||||
|
<div
|
||||||
|
className={`flex flex-col items-center justify-center min-w-[60px] py-2 px-1 rounded-lg transition-all duration-200 ${
|
||||||
|
active
|
||||||
|
? 'bg-blue-50 text-blue-600'
|
||||||
|
: 'text-gray-600 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
data-testid={`nav-${item.path.replace('/', 'home')}`}
|
||||||
|
>
|
||||||
|
<Icon className={`w-5 h-5 ${active ? 'text-blue-600' : 'text-gray-600'}`} />
|
||||||
|
<span className={`text-xs mt-1 font-medium ${
|
||||||
|
active ? 'text-blue-600' : 'text-gray-600'
|
||||||
|
}`}>
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* Menu "Plus" */}
|
||||||
|
<Sheet open={moreSheetOpen} onOpenChange={setMoreSheetOpen}>
|
||||||
|
<SheetTrigger asChild>
|
||||||
|
<div
|
||||||
|
className="flex flex-col items-center justify-center min-w-[60px] py-2 px-1 rounded-lg transition-all duration-200 text-gray-600 hover:bg-gray-50 cursor-pointer"
|
||||||
|
data-testid="nav-more"
|
||||||
|
>
|
||||||
|
<MoreHorizontal className="w-5 h-5 text-gray-600" />
|
||||||
|
<span className="text-xs mt-1 font-medium text-gray-600">Plus</span>
|
||||||
|
</div>
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent side="bottom" className="h-auto max-h-[60vh] rounded-t-lg">
|
||||||
|
<SheetHeader>
|
||||||
|
<SheetTitle>Navigation</SheetTitle>
|
||||||
|
<SheetDescription>
|
||||||
|
Accédez aux autres sections de l'application
|
||||||
|
</SheetDescription>
|
||||||
|
</SheetHeader>
|
||||||
|
<div className="grid grid-cols-3 gap-4 mt-6 pb-4">
|
||||||
|
{filteredMoreItems.map((item) => {
|
||||||
|
const Icon = item.icon
|
||||||
|
const active = isActive(item.path)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.path}
|
||||||
|
href={item.path}
|
||||||
|
onClick={() => setMoreSheetOpen(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`flex flex-col items-center justify-center p-4 rounded-lg transition-all duration-200 min-h-[80px] ${
|
||||||
|
active
|
||||||
|
? 'bg-blue-50 text-blue-600 border-2 border-blue-200'
|
||||||
|
: 'bg-gray-50 text-gray-700 hover:bg-gray-100 border-2 border-transparent'
|
||||||
|
}`}
|
||||||
|
data-testid={`more-nav-${item.path.replace('/', '')}`}
|
||||||
|
>
|
||||||
|
<Icon className={`w-6 h-6 ${active ? 'text-blue-600' : 'text-gray-600'}`} />
|
||||||
|
<span className={`text-sm mt-2 font-medium text-center ${
|
||||||
|
active ? 'text-blue-600' : 'text-gray-700'
|
||||||
|
}`}>
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -105,6 +105,17 @@ function RouterProduction() {
|
|||||||
<Route path="/database-debug" component={Utilities} />
|
<Route path="/database-debug" component={Utilities} />
|
||||||
<Route path="/weather-settings" component={Utilities} />
|
<Route path="/weather-settings" component={Utilities} />
|
||||||
|
|
||||||
|
{/* Redirection depuis /auth vers dashboard après authentification */}
|
||||||
|
<Route path="/auth">
|
||||||
|
{() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
console.log('🔄 Authenticated user on /auth, redirecting to dashboard');
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
return <Dashboard />;
|
||||||
|
}}
|
||||||
|
</Route>
|
||||||
|
|
||||||
<Route path="/" component={Dashboard} />
|
<Route path="/" component={Dashboard} />
|
||||||
<Route component={NotFound} />
|
<Route component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { useScreenSize } from './use-screen-size'
|
||||||
|
|
||||||
|
export interface PhoneModeConfig {
|
||||||
|
isPhoneMode: boolean
|
||||||
|
setPhoneMode: (enabled: boolean) => void
|
||||||
|
canUsePhoneMode: boolean
|
||||||
|
autoSuggestPhoneMode: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePhoneMode(): PhoneModeConfig {
|
||||||
|
const { isMobile, dimensions } = useScreenSize()
|
||||||
|
|
||||||
|
// Déterminer si l'appareil peut bénéficier du mode téléphone
|
||||||
|
const canUsePhoneMode = isMobile || dimensions.width <= 480
|
||||||
|
|
||||||
|
// Auto-suggestion pour très petits écrans (< 400px)
|
||||||
|
const autoSuggestPhoneMode = dimensions.width < 400
|
||||||
|
|
||||||
|
const [isPhoneMode, setIsPhoneMode] = useState<boolean>(() => {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('phoneMode')
|
||||||
|
if (saved !== null) {
|
||||||
|
return JSON.parse(saved)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-activation pour très petits écrans
|
||||||
|
if (autoSuggestPhoneMode) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading phone mode from localStorage:', error)
|
||||||
|
return autoSuggestPhoneMode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const setPhoneMode = (enabled: boolean) => {
|
||||||
|
setIsPhoneMode(enabled)
|
||||||
|
try {
|
||||||
|
localStorage.setItem('phoneMode', JSON.stringify(enabled))
|
||||||
|
console.log('📱 Phone mode preference saved:', enabled)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving phone mode to localStorage:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Effect pour activer automatiquement le mode téléphone selon la largeur
|
||||||
|
useEffect(() => {
|
||||||
|
const saved = localStorage.getItem('phoneMode')
|
||||||
|
if (saved !== null) return // L'utilisateur a une préférence explicite, ne pas override
|
||||||
|
|
||||||
|
const shouldEnable = dimensions.width < 400
|
||||||
|
if (isPhoneMode !== shouldEnable) {
|
||||||
|
console.log('📱 Auto-toggling phone mode:', {
|
||||||
|
width: dimensions.width,
|
||||||
|
shouldEnable,
|
||||||
|
currentMode: isPhoneMode
|
||||||
|
})
|
||||||
|
setIsPhoneMode(shouldEnable)
|
||||||
|
}
|
||||||
|
}, [dimensions.width, isPhoneMode])
|
||||||
|
|
||||||
|
// Effect pour logger les changements de mode
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('📱 Phone mode state:', {
|
||||||
|
isPhoneMode,
|
||||||
|
canUsePhoneMode,
|
||||||
|
autoSuggestPhoneMode,
|
||||||
|
screenWidth: dimensions.width,
|
||||||
|
isMobile
|
||||||
|
})
|
||||||
|
}, [isPhoneMode, canUsePhoneMode, autoSuggestPhoneMode, dimensions.width, isMobile])
|
||||||
|
|
||||||
|
return {
|
||||||
|
isPhoneMode,
|
||||||
|
setPhoneMode,
|
||||||
|
canUsePhoneMode,
|
||||||
|
autoSuggestPhoneMode
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -396,3 +396,92 @@ button[data-day-weekend="true"],
|
|||||||
.bg-gray-150 {
|
.bg-gray-150 {
|
||||||
background-color: #f1f3f4;
|
background-color: #f1f3f4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Styles pour le mode téléphone */
|
||||||
|
.phone-mode {
|
||||||
|
/* Classe principale pour le mode téléphone */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Safe area pour la bottom navigation */
|
||||||
|
.safe-area-pb {
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optimisations pour le mode téléphone */
|
||||||
|
.layout-container.phone-mode {
|
||||||
|
/* Assurer que le layout prend toute la hauteur */
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-mode main {
|
||||||
|
/* Optimisation pour le scroll sur téléphone */
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-mode .main-content {
|
||||||
|
/* Padding bottom pour éviter que le contenu soit caché par la bottom nav */
|
||||||
|
padding-bottom: calc(80px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation de transition pour le basculement de mode */
|
||||||
|
.layout-container {
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styles spécifiques pour la bottom navigation */
|
||||||
|
.bottom-nav {
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode pour la bottom navigation */
|
||||||
|
.dark .bottom-nav {
|
||||||
|
background-color: rgba(26, 26, 26, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optimisation des cartes pour le mode téléphone */
|
||||||
|
.phone-mode .card {
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optimisation des boutons pour le mode téléphone */
|
||||||
|
.phone-mode button {
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style pour les éléments interactifs en mode téléphone */
|
||||||
|
.phone-mode .interactive-element {
|
||||||
|
min-height: 48px;
|
||||||
|
min-width: 48px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adaptation des formulaires pour téléphone */
|
||||||
|
.phone-mode .form-container {
|
||||||
|
padding: 16px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-mode input,
|
||||||
|
.phone-mode select,
|
||||||
|
.phone-mode textarea {
|
||||||
|
min-height: 48px;
|
||||||
|
font-size: 16px; /* Évite le zoom automatique sur iOS */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optimisation des modals pour téléphone */
|
||||||
|
.phone-mode .modal-content {
|
||||||
|
margin: 8px;
|
||||||
|
max-height: calc(100vh - 16px);
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style pour les alertes en mode téléphone */
|
||||||
|
.phone-mode .alert {
|
||||||
|
margin: 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user