mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Merge pull request #438 from R0m1k3/1.1
Remove phone icon and improve horizontal overflow on mobile devices
This commit is contained in:
3 files changed
+39
-166
No files matched your search
@@ -3,14 +3,12 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { useLocation } from "wouter";
|
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, Smartphone, Monitor } from "lucide-react";
|
import { LogOut, Store, Menu, AlertTriangle } 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";
|
||||||
@@ -26,7 +24,6 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
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
|
||||||
@@ -125,9 +122,9 @@ 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 ${isPhoneMode ? 'phone-mode' : ''}`}>
|
<div className="layout-container flex bg-gray-50">
|
||||||
{/* Mobile overlay for normal mode */}
|
{/* Mobile overlay for normal mode */}
|
||||||
{!isPhoneMode && isMobile && mobileMenuOpen && (
|
{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)}
|
||||||
@@ -135,25 +132,21 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Sidebar for normal mode */}
|
{/* Sidebar for normal mode */}
|
||||||
{!isPhoneMode && <Sidebar />}
|
<Sidebar />
|
||||||
|
|
||||||
<main className={`flex-1 flex flex-col h-full ${
|
<main className={`flex-1 flex flex-col h-full ${
|
||||||
// Ajustement des marges selon le mode
|
// Ajustement des marges selon le mode
|
||||||
isPhoneMode
|
isMobile
|
||||||
? 'ml-0 mb-20' // Mode téléphone : pas de marge gauche, marge bas pour bottom nav
|
|
||||||
: isMobile
|
|
||||||
? 'ml-0'
|
? 'ml-0'
|
||||||
: (isTablet && !sidebarCollapsed) ? 'ml-48' : 'ml-0'
|
: (isTablet && !sidebarCollapsed) ? 'ml-48' : 'ml-0'
|
||||||
}`}>
|
}`}>
|
||||||
{/* Header with mobile menu and store selector */}
|
{/* Header with mobile menu and store selector */}
|
||||||
<header className={`${
|
<header className={`h-16 bg-white border-b border-gray-200 flex items-center justify-between ${
|
||||||
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 (only in normal mode) */}
|
{/* Mobile menu button (only in normal mode) */}
|
||||||
{!isPhoneMode && isMobile && (
|
{isMobile && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -169,47 +162,13 @@ 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`}>
|
||||||
{/* En mode téléphone, affichage simplifié */}
|
<WeatherWidget />
|
||||||
{isPhoneMode ? (
|
{!isMobile && <DateWidget />}
|
||||||
<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 */}
|
||||||
{!isPhoneMode && user && (user.role === 'admin' || user.role === 'directeur' || user.role === 'manager') && stores.length > 0 && (
|
{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
|
||||||
@@ -299,16 +258,12 @@ 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 ${
|
||||||
isPhoneMode
|
isMobileOrTablet ? 'p-3' : 'p-6'
|
||||||
? '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>
|
||||||
);
|
);
|
||||||
|
|||||||
+14
-1
@@ -64,11 +64,24 @@
|
|||||||
@apply font-sans antialiased bg-surface text-foreground;
|
@apply font-sans antialiased bg-surface text-foreground;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
height: 100%;
|
min-height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Éviter le débordement horizontal sur mobile */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forcer les conteneurs à ne pas déborder */
|
||||||
|
.container, .main-content, [data-testid*="task"] {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Forcer la cohérence des largeurs de sidebar */
|
/* Forcer la cohérence des largeurs de sidebar */
|
||||||
|
|||||||
+14
-109
@@ -1565,37 +1565,10 @@ export default function Tasks() {
|
|||||||
|
|
||||||
{/* Modal d'édition */}
|
{/* Modal d'édition */}
|
||||||
{showEditModal && selectedTask && (
|
{showEditModal && selectedTask && (
|
||||||
<div style={{
|
<div className="fixed inset-0 bg-black bg-opacity-50 z-[1000] flex items-start sm:items-center justify-center p-4">
|
||||||
position: 'fixed',
|
<div className="bg-white dark:bg-gray-800 rounded-lg max-w-full sm:max-w-2xl w-full max-h-[calc(100vh-2rem)] overflow-y-auto shadow-2xl">
|
||||||
top: 0,
|
<div className="p-6 pb-4 border-b border-gray-200 dark:border-gray-700 sticky top-0 bg-white dark:bg-gray-800">
|
||||||
left: 0,
|
<h2 className="text-lg font-semibold m-0 pr-10 dark:text-white">
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
zIndex: 1000,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center'
|
|
||||||
}}>
|
|
||||||
<div style={{
|
|
||||||
backgroundColor: 'white',
|
|
||||||
borderRadius: '0px',
|
|
||||||
maxWidth: '600px',
|
|
||||||
width: '90%',
|
|
||||||
maxHeight: '90vh',
|
|
||||||
overflow: 'auto',
|
|
||||||
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)'
|
|
||||||
}}>
|
|
||||||
<div style={{
|
|
||||||
padding: '24px 24px 16px',
|
|
||||||
borderBottom: '1px solid #e5e7eb',
|
|
||||||
position: 'relative'
|
|
||||||
}}>
|
|
||||||
<h2 style={{
|
|
||||||
fontSize: '18px',
|
|
||||||
fontWeight: '600',
|
|
||||||
margin: '0'
|
|
||||||
}}>
|
|
||||||
Modifier la tâche
|
Modifier la tâche
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
@@ -1603,21 +1576,7 @@ export default function Tasks() {
|
|||||||
setShowEditModal(false);
|
setShowEditModal(false);
|
||||||
setSelectedTask(null);
|
setSelectedTask(null);
|
||||||
}}
|
}}
|
||||||
style={{
|
className="absolute top-4 right-4 bg-transparent border-none text-xl cursor-pointer p-2 rounded text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-colors"
|
||||||
position: 'absolute',
|
|
||||||
top: '16px',
|
|
||||||
right: '16px',
|
|
||||||
background: 'none',
|
|
||||||
border: 'none',
|
|
||||||
fontSize: '20px',
|
|
||||||
cursor: 'pointer',
|
|
||||||
padding: '4px',
|
|
||||||
borderRadius: '0px',
|
|
||||||
color: '#6b7280',
|
|
||||||
transition: 'color 0.2s'
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => (e.target as HTMLButtonElement).style.color = '#374151'}
|
|
||||||
onMouseLeave={(e) => (e.target as HTMLButtonElement).style.color = '#6b7280'}
|
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
@@ -1637,86 +1596,32 @@ export default function Tasks() {
|
|||||||
|
|
||||||
{/* Modal de confirmation de suppression */}
|
{/* Modal de confirmation de suppression */}
|
||||||
{showDeleteModal && taskToDelete && (
|
{showDeleteModal && taskToDelete && (
|
||||||
<div style={{
|
<div className="fixed inset-0 bg-black bg-opacity-50 z-[1000] flex items-center justify-center p-4">
|
||||||
position: 'fixed',
|
<div className="bg-white dark:bg-gray-800 rounded-lg max-w-sm w-full p-6 shadow-2xl mx-4">
|
||||||
top: 0,
|
<h2 className="text-lg font-semibold mb-4 text-red-600 dark:text-red-400 flex items-center gap-2">
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
zIndex: 1000,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center'
|
|
||||||
}}>
|
|
||||||
<div style={{
|
|
||||||
backgroundColor: 'white',
|
|
||||||
borderRadius: '0px',
|
|
||||||
maxWidth: '400px',
|
|
||||||
width: '90%',
|
|
||||||
padding: '24px',
|
|
||||||
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)'
|
|
||||||
}}>
|
|
||||||
<h2 style={{
|
|
||||||
fontSize: '18px',
|
|
||||||
fontWeight: '600',
|
|
||||||
margin: '0 0 16px 0',
|
|
||||||
color: '#dc2626',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '8px'
|
|
||||||
}}>
|
|
||||||
⚠️ Confirmer la suppression
|
⚠️ Confirmer la suppression
|
||||||
</h2>
|
</h2>
|
||||||
<div style={{ marginBottom: '24px' }}>
|
<div className="mb-6">
|
||||||
<p style={{
|
<p className="text-gray-700 dark:text-gray-300 leading-relaxed mb-2">
|
||||||
color: '#374151',
|
|
||||||
lineHeight: '1.5',
|
|
||||||
margin: '0 0 8px 0'
|
|
||||||
}}>
|
|
||||||
Êtes-vous sûr de vouloir supprimer la tâche "{taskToDelete.title}" ?
|
Êtes-vous sûr de vouloir supprimer la tâche "{taskToDelete.title}" ?
|
||||||
</p>
|
</p>
|
||||||
<p style={{
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
fontSize: '14px',
|
|
||||||
color: '#6b7280',
|
|
||||||
margin: '0'
|
|
||||||
}}>
|
|
||||||
Cette action est irréversible.
|
Cette action est irréversible.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div className="flex gap-3 flex-col sm:flex-row">
|
||||||
display: 'flex',
|
|
||||||
gap: '12px',
|
|
||||||
justifyContent: 'flex-end'
|
|
||||||
}}>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowDeleteModal(false);
|
setShowDeleteModal(false);
|
||||||
setTaskToDelete(null);
|
setTaskToDelete(null);
|
||||||
}}
|
}}
|
||||||
style={{
|
className="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-600 order-2 sm:order-1"
|
||||||
padding: '8px 16px',
|
|
||||||
border: '1px solid #d1d5db',
|
|
||||||
borderRadius: '0px',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
cursor: 'pointer'
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleConfirmDelete}
|
onClick={handleConfirmDelete}
|
||||||
style={{
|
className="px-4 py-2 border-none rounded-md bg-red-600 text-white cursor-pointer hover:bg-red-700 flex items-center justify-center gap-2 order-1 sm:order-2"
|
||||||
padding: '8px 16px',
|
|
||||||
border: 'none',
|
|
||||||
borderRadius: '0px',
|
|
||||||
backgroundColor: '#dc2626',
|
|
||||||
color: 'white',
|
|
||||||
cursor: 'pointer',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '8px'
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
🗑️ Supprimer
|
🗑️ Supprimer
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in new issue
Block a user