mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-21 05:58:23 +02:00
Refactor store context and improve responsiveness across the application
Refactored the `useStore` hook to be imported from a dedicated `StoreContext` to improve code organization and maintainability. Also, enhanced responsive design for better usability on various screen sizes, including tablet-specific adjustments for the sidebar and header. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 95e8aab4-f39f-4b48-aa57-a759ec15ba3d Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/95e8aab4-f39f-4b48-aa57-a759ec15ba3d/A90CXrX
This commit is contained in:
1 parent
d2f2a532a5
commit
d0cecf90f3
29 files changed
+957
-254
No files matched your search
@@ -1,35 +1,17 @@
|
||||
import { ReactNode, useState, createContext, useContext, useEffect } from "react";
|
||||
import { ReactNode, useState, useEffect } 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, Menu, AlertTriangle } from "lucide-react";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { useScreenSize } from "@/hooks/use-screen-size";
|
||||
import { StoreProvider } from "@/contexts/StoreContext";
|
||||
import Sidebar from "./Sidebar";
|
||||
import WeatherWidget from "./WeatherWidget";
|
||||
import DateWidget from "./DateWidget";
|
||||
import type { Group } from "@shared/schema";
|
||||
|
||||
interface StoreContextType {
|
||||
selectedStoreId: number | null;
|
||||
setSelectedStoreId: (storeId: number | null) => void;
|
||||
stores: Group[];
|
||||
sidebarCollapsed: boolean;
|
||||
setSidebarCollapsed: (collapsed: boolean) => void;
|
||||
mobileMenuOpen: boolean;
|
||||
setMobileMenuOpen: (open: boolean) => void;
|
||||
storeInitialized: boolean;
|
||||
}
|
||||
|
||||
const StoreContext = createContext<StoreContextType | undefined>(undefined);
|
||||
|
||||
export const useStore = () => {
|
||||
const context = useContext(StoreContext);
|
||||
if (!context) {
|
||||
throw new Error("useStore must be used within a StoreProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface LayoutProps {
|
||||
children: ReactNode;
|
||||
@@ -39,6 +21,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
const { user } = useAuthUnified();
|
||||
const queryClient = useQueryClient();
|
||||
const isMobile = useIsMobile();
|
||||
const { screenSize, isMobileOrTablet, isTablet } = useScreenSize();
|
||||
|
||||
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
|
||||
// Restaurer le selectedStoreId depuis localStorage si disponible
|
||||
@@ -136,7 +119,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<StoreContext.Provider 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">
|
||||
{/* Mobile overlay */}
|
||||
{isMobile && mobileMenuOpen && (
|
||||
@@ -148,32 +131,42 @@ export default function Layout({ children }: LayoutProps) {
|
||||
|
||||
<Sidebar />
|
||||
|
||||
<main className="flex-1 flex flex-col h-full">
|
||||
<main className={`flex-1 flex flex-col h-full ${
|
||||
// Ajuster la marge gauche selon la taille de l'écran et l'état de la sidebar
|
||||
isMobile ? 'ml-0' :
|
||||
isTablet ? (sidebarCollapsed ? 'ml-16' : 'ml-48') :
|
||||
(sidebarCollapsed ? 'ml-16' : 'ml-64')
|
||||
}`}>
|
||||
{/* 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">
|
||||
<header className={`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 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setMobileMenuOpen(true)}
|
||||
className="h-8 w-8 p-0"
|
||||
className="min-h-[44px] min-w-[44px] p-0 flex-shrink-0"
|
||||
data-testid="button-mobile-menu"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Weather and Date widgets */}
|
||||
<div className="flex items-center gap-3">
|
||||
{/* Weather and Date widgets - responsive */}
|
||||
<div className={`flex items-center ${
|
||||
isMobile ? 'gap-1' : 'gap-3'
|
||||
} min-w-0`}>
|
||||
<WeatherWidget />
|
||||
<DateWidget />
|
||||
{!isMobile && <DateWidget />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Store selector for admin, directeur, and manager - responsive */}
|
||||
{user && (user.role === 'admin' || user.role === 'directeur' || user.role === 'manager') && stores.length > 0 && (
|
||||
<div className="flex items-center gap-2">
|
||||
<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
|
||||
value={selectedStoreId?.toString() || (user.role === 'admin' ? "all" : "")}
|
||||
@@ -206,8 +199,12 @@ export default function Layout({ children }: LayoutProps) {
|
||||
setSelectedStoreId(newStoreId);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-32 sm:w-64 border border-gray-300">
|
||||
<SelectValue placeholder="Sélectionnez un magasin" />
|
||||
<SelectTrigger className={`border border-gray-300 min-h-[44px] ${
|
||||
isMobile ? 'w-28 text-xs' :
|
||||
isTablet ? 'w-40 text-sm' :
|
||||
'w-64 text-sm'
|
||||
}`} data-testid="select-store">
|
||||
<SelectValue placeholder="Magasin" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{/* Only show "Tous les magasins" option for admin */}
|
||||
@@ -215,7 +212,9 @@ export default function Layout({ children }: LayoutProps) {
|
||||
<SelectItem value="all">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 bg-gray-400"></div>
|
||||
<span>Tous les magasins</span>
|
||||
<span className={isMobile ? 'text-xs' : 'text-sm'}>
|
||||
{isMobile ? 'Tous' : 'Tous les magasins'}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
@@ -226,7 +225,9 @@ export default function Layout({ children }: LayoutProps) {
|
||||
className="w-3 h-3"
|
||||
style={{ backgroundColor: store.color || '#gray-400' }}
|
||||
/>
|
||||
<span>{store.name}</span>
|
||||
<span className={`${isMobile ? 'text-xs' : 'text-sm'} truncate max-w-[120px]`}>
|
||||
{store.name}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
@@ -253,11 +254,13 @@ export default function Layout({ children }: LayoutProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex-1 bg-gray-50 p-6 h-full overflow-y-auto">
|
||||
<div className={`flex-1 bg-gray-50 h-full overflow-y-auto ${
|
||||
isMobileOrTablet ? 'p-3' : 'p-6'
|
||||
}`}>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</StoreContext.Provider>
|
||||
</StoreProvider>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user