Merge pull request #432 from R0m1k3/1.1

Refactor store context and improve responsiveness across the application
This commit is contained in:
LogiFlow authored and GitHub committed 2025-09-11 08:22:47 +02:00
commit 3976f151e2
29 files changed
+957 -254

No files matched your search

+4
View File
@@ -15,6 +15,10 @@ run = ["npm", "run", "start"]
localPort = 5000 localPort = 5000
externalPort = 80 externalPort = 80
[[ports]]
localPort = 45105
externalPort = 3000
[workflows] [workflows]
runButton = "Project" runButton = "Project"
+1 -1
View File
@@ -39,7 +39,7 @@ import {
Sparkles Sparkles
} from "lucide-react"; } from "lucide-react";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { safeFormat, safeDate } from "@/lib/dateUtils"; import { safeFormat, safeDate } from "@/lib/dateUtils";
import { import {
insertAnnouncementSchema, insertAnnouncementSchema,
+1 -1
View File
@@ -23,7 +23,7 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { insertCustomerOrderFrontendSchema, type CustomerOrderWithRelations, type Group, type Supplier } from "@shared/schema"; import { insertCustomerOrderFrontendSchema, type CustomerOrderWithRelations, type Group, type Supplier } from "@shared/schema";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
const customerOrderFormSchema = z.object({ const customerOrderFormSchema = z.object({
orderTaker: z.string().min(1, "Qui a pris la commande est requis"), orderTaker: z.string().min(1, "Qui a pris la commande est requis"),
+39 -36
View File
@@ -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 { useQuery, useQueryClient } from "@tanstack/react-query";
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 } 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 { StoreProvider } from "@/contexts/StoreContext";
import Sidebar from "./Sidebar"; import Sidebar from "./Sidebar";
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";
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 { interface LayoutProps {
children: ReactNode; children: ReactNode;
@@ -39,6 +21,7 @@ export default function Layout({ children }: LayoutProps) {
const { user } = useAuthUnified(); const { user } = useAuthUnified();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { screenSize, isMobileOrTablet, isTablet } = useScreenSize();
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
@@ -136,7 +119,7 @@ export default function Layout({ children }: LayoutProps) {
}; };
return ( 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"> <div className="layout-container flex bg-gray-50">
{/* Mobile overlay */} {/* Mobile overlay */}
{isMobile && mobileMenuOpen && ( {isMobile && mobileMenuOpen && (
@@ -148,32 +131,42 @@ export default function Layout({ children }: LayoutProps) {
<Sidebar /> <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 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"> <header className={`h-16 bg-white border-b border-gray-200 flex items-center justify-between ${
<div className="flex items-center gap-4"> isMobileOrTablet ? 'px-3' : 'px-6'
}`}>
<div className="flex items-center gap-2 sm:gap-4 min-w-0 flex-1">
{/* Mobile menu button */} {/* Mobile menu button */}
{isMobile && ( {isMobile && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => setMobileMenuOpen(true)} 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" /> <Menu className="h-5 w-5" />
</Button> </Button>
)} )}
{/* Weather and Date widgets */} {/* Weather and Date widgets - responsive */}
<div className="flex items-center gap-3"> <div className={`flex items-center ${
isMobile ? 'gap-1' : 'gap-3'
} min-w-0`}>
<WeatherWidget /> <WeatherWidget />
<DateWidget /> {!isMobile && <DateWidget />}
</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 && ( {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" /> <Store className="h-4 w-4 text-gray-500 hidden sm:block" />
<Select <Select
value={selectedStoreId?.toString() || (user.role === 'admin' ? "all" : "")} value={selectedStoreId?.toString() || (user.role === 'admin' ? "all" : "")}
@@ -206,8 +199,12 @@ export default function Layout({ children }: LayoutProps) {
setSelectedStoreId(newStoreId); setSelectedStoreId(newStoreId);
}} }}
> >
<SelectTrigger className="w-32 sm:w-64 border border-gray-300"> <SelectTrigger className={`border border-gray-300 min-h-[44px] ${
<SelectValue placeholder="Sélectionnez un magasin" /> isMobile ? 'w-28 text-xs' :
isTablet ? 'w-40 text-sm' :
'w-64 text-sm'
}`} data-testid="select-store">
<SelectValue placeholder="Magasin" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{/* Only show "Tous les magasins" option for admin */} {/* Only show "Tous les magasins" option for admin */}
@@ -215,7 +212,9 @@ export default function Layout({ children }: LayoutProps) {
<SelectItem value="all"> <SelectItem value="all">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-3 h-3 bg-gray-400"></div> <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> </div>
</SelectItem> </SelectItem>
)} )}
@@ -226,7 +225,9 @@ export default function Layout({ children }: LayoutProps) {
className="w-3 h-3" className="w-3 h-3"
style={{ backgroundColor: store.color || '#gray-400' }} 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> </div>
</SelectItem> </SelectItem>
))} ))}
@@ -253,11 +254,13 @@ export default function Layout({ children }: LayoutProps) {
</div> </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} {children}
</div> </div>
</main> </main>
</div> </div>
</StoreContext.Provider> </StoreProvider>
); );
} }
+77 -19
View File
@@ -1,8 +1,9 @@
import { Link, useLocation } from "wouter"; import { Link, useLocation } from "wouter";
import { useAuthSimple } from "@/hooks/useAuthSimple"; import { useAuthSimple } from "@/hooks/useAuthSimple";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useStore } from "./Layout"; import { useStore } from "@/contexts/StoreContext";
import { useIsMobile } from "@/hooks/use-mobile"; import { useIsMobile } from "@/hooks/use-mobile";
import { useScreenSize } from "@/hooks/use-screen-size";
import { useState } from "react"; import { useState } from "react";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
@@ -41,6 +42,7 @@ export default function Sidebar() {
const [location] = useLocation(); const [location] = useLocation();
const { sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen } = useStore(); const { sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen } = useStore();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { screenSize, isMobileOrTablet, isTablet } = useScreenSize();
const [showBapModal, setShowBapModal] = useState(false); const [showBapModal, setShowBapModal] = useState(false);
const [selectedFile, setSelectedFile] = useState<File | null>(null); const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [selectedRecipient, setSelectedRecipient] = useState<string>(''); const [selectedRecipient, setSelectedRecipient] = useState<string>('');
@@ -338,12 +340,24 @@ export default function Sidebar() {
// Classes responsives pour la sidebar avec classes CSS forcées pour cohérence // Classes responsives adaptées avec support tablette
const sidebarClasses = isMobile const getSidebarClasses = () => {
? `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 ${ if (isMobile) {
return `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' mobileMenuOpen ? 'translate-x-0' : '-translate-x-full'
}` }`;
: `${sidebarCollapsed ? 'sidebar-collapsed' : 'sidebar-expanded'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`; }
// Mode tablette : semi-collapsed par défaut ou collapsed si forcé
if (isTablet) {
return `${sidebarCollapsed ? 'sidebar-collapsed' : 'sidebar-tablet'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`;
}
// Mode desktop standard
return `${sidebarCollapsed ? 'sidebar-collapsed' : 'sidebar-expanded'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`;
};
const sidebarClasses = getSidebarClasses();
// Debug log pour vérifier l'état de la sidebar // Debug log pour vérifier l'état de la sidebar
console.log('🔧 Sidebar Debug:', { console.log('🔧 Sidebar Debug:', {
@@ -542,16 +556,32 @@ export default function Sidebar() {
return ( return (
<Link key={item.path} href={item.path}> <Link key={item.path} href={item.path}>
<div <div
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 ${ className={`flex items-center transition-colors hover:bg-gray-100 ${
// Espacement adaptatif selon l'écran et l'état
sidebarCollapsed && !isMobile
? 'px-3 py-3 justify-center min-h-[44px]'
: isMobileOrTablet
? 'px-4 py-3 min-h-[48px]'
: 'px-3 py-2 min-h-[40px]'
} text-sm font-medium ${
active active
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700' ? 'bg-gray-100 text-gray-900 border-r-2 border-blue-600'
: 'text-gray-700' : 'text-gray-700'
}`} }`}
title={sidebarCollapsed && !isMobile ? item.label : undefined} title={sidebarCollapsed && !isMobile ? item.label : undefined}
onClick={handleMenuClick} onClick={handleMenuClick}
> >
<Icon className={`h-4 w-4 ${sidebarCollapsed && !isMobile ? '' : 'mr-3'}`} /> <Icon className={`${
{(!sidebarCollapsed || isMobile) && item.label} // Tailles d'icônes adaptatives
isMobile ? 'h-5 w-5' :
isTablet ? 'h-6 w-6' :
'h-5 w-5'
} ${sidebarCollapsed && !isMobile ? '' : 'mr-3'}`} />
{(!sidebarCollapsed || isMobile) && (
<span className={`${isMobileOrTablet ? 'text-base' : 'text-sm'} font-medium`}>
{item.label}
</span>
)}
</div> </div>
</Link> </Link>
); );
@@ -582,15 +612,29 @@ export default function Sidebar() {
return ( return (
<div key={item.path}> <div key={item.path}>
<div <div
className={`flex items-center cursor-pointer ${sidebarCollapsed ? 'px-3 py-3 justify-center' : 'px-3 py-2'} text-sm font-medium transition-colors hover:bg-gray-100 text-gray-700`} className={`flex items-center cursor-pointer transition-colors hover:bg-gray-100 text-gray-700 ${
title={sidebarCollapsed ? item.label : undefined} sidebarCollapsed && !isMobile
? 'px-3 py-3 justify-center min-h-[44px]'
: isMobileOrTablet
? 'px-4 py-3 min-h-[48px]'
: 'px-3 py-2 min-h-[40px]'
} text-sm font-medium`}
title={sidebarCollapsed && !isMobile ? item.label : undefined}
onClick={() => { onClick={() => {
setShowBapModal(true); setShowBapModal(true);
if (isMobile) setMobileMenuOpen(false); if (isMobile) setMobileMenuOpen(false);
}} }}
> >
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} /> <Icon className={`${
{!sidebarCollapsed && item.label} isMobile ? 'h-5 w-5' :
isTablet ? 'h-6 w-6' :
'h-5 w-5'
} ${sidebarCollapsed && !isMobile ? '' : 'mr-3'}`} />
{(!sidebarCollapsed || isMobile) && (
<span className={`${isMobileOrTablet ? 'text-base' : 'text-sm'} font-medium`}>
{item.label}
</span>
)}
</div> </div>
</div> </div>
); );
@@ -599,16 +643,30 @@ export default function Sidebar() {
return ( return (
<Link key={item.path} href={item.path}> <Link key={item.path} href={item.path}>
<div <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 transition-colors hover:bg-gray-100 ${
sidebarCollapsed && !isMobile
? 'px-3 py-3 justify-center min-h-[44px]'
: isMobileOrTablet
? 'px-4 py-3 min-h-[48px]'
: 'px-3 py-2 min-h-[40px]'
} text-sm font-medium ${
active active
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700' ? 'bg-gray-100 text-gray-900 border-r-2 border-blue-600'
: 'text-gray-700' : 'text-gray-700'
}`} }`}
title={sidebarCollapsed ? item.label : undefined} title={sidebarCollapsed && !isMobile ? item.label : undefined}
onClick={() => console.log(`Navigating to admin: ${item.path}`)} onClick={() => console.log(`Navigating to admin: ${item.path}`)}
> >
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} /> <Icon className={`${
{!sidebarCollapsed && item.label} isMobile ? 'h-5 w-5' :
isTablet ? 'h-6 w-6' :
'h-5 w-5'
} ${sidebarCollapsed && !isMobile ? '' : 'mr-3'}`} />
{(!sidebarCollapsed || isMobile) && (
<span className={`${isMobileOrTablet ? 'text-base' : 'text-sm'} font-medium`}>
{item.label}
</span>
)}
</div> </div>
</Link> </Link>
); );
+1 -1
View File
@@ -1,7 +1,7 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { TrendingUp, Package, Truck, Clock } from "lucide-react"; import { TrendingUp, Package, Truck, Clock } from "lucide-react";
import { useStore } from "./Layout"; import { useStore } from "@/contexts/StoreContext";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
interface StatsPanelProps { interface StatsPanelProps {
@@ -7,7 +7,7 @@ import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { isUnauthorizedError } from "@/lib/authUtils"; import { isUnauthorizedError } from "@/lib/authUtils";
@@ -7,7 +7,7 @@ import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { isUnauthorizedError } from "@/lib/authUtils"; import { isUnauthorizedError } from "@/lib/authUtils";
+1 -1
View File
@@ -3,7 +3,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query"; import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -13,7 +13,7 @@ import {
import { format, addDays, isAfter, isBefore } from "date-fns"; import { format, addDays, isAfter, isBefore } from "date-fns";
import { fr } from "date-fns/locale"; import { fr } from "date-fns/locale";
import { useLocation } from "wouter"; import { useLocation } from "wouter";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { Task } from "@shared/schema"; import { Task } from "@shared/schema";
type TaskWithRelations = Task & { type TaskWithRelations = Task & {
@@ -3,7 +3,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query"; import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -3,7 +3,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query"; import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
+32
View File
@@ -0,0 +1,32 @@
import { ReactNode, createContext, useContext } from "react";
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 StoreProviderProps {
children: ReactNode;
value: StoreContextType;
}
export function StoreProvider({ children, value }: StoreProviderProps) {
return <StoreContext.Provider value={value}>{children}</StoreContext.Provider>;
}
+58
View File
@@ -0,0 +1,58 @@
import { useState, useEffect } from 'react'
export type ScreenSize = 'mobile' | 'tablet' | 'desktop'
export interface ScreenBreakpoints {
mobile: number
tablet: number
desktop: number
}
const defaultBreakpoints: ScreenBreakpoints = {
mobile: 768,
tablet: 1024,
desktop: 1280
}
export function useScreenSize(breakpoints: ScreenBreakpoints = defaultBreakpoints) {
const [screenSize, setScreenSize] = useState<ScreenSize>('desktop')
const [dimensions, setDimensions] = useState({
width: typeof window !== 'undefined' ? window.innerWidth : 1200,
height: typeof window !== 'undefined' ? window.innerHeight : 800
})
useEffect(() => {
const getScreenSize = (width: number): ScreenSize => {
if (width < breakpoints.mobile) return 'mobile'
if (width <= breakpoints.tablet) return 'tablet' // Inclut 1024px comme tablette
return 'desktop'
}
const updateScreenSize = () => {
const width = window.innerWidth
const height = window.innerHeight
setDimensions({ width, height })
setScreenSize(getScreenSize(width))
}
// Set initial values
updateScreenSize()
// Add event listener
window.addEventListener('resize', updateScreenSize)
// Cleanup
return () => window.removeEventListener('resize', updateScreenSize)
}, [breakpoints])
return {
screenSize,
dimensions,
isMobile: screenSize === 'mobile',
isTablet: screenSize === 'tablet',
isDesktop: screenSize === 'desktop',
isMobileOrTablet: screenSize === 'mobile' || screenSize === 'tablet',
isTabletOrDesktop: screenSize === 'tablet' || screenSize === 'desktop'
}
}
+11
View File
@@ -80,6 +80,17 @@
max-width: 4rem !important; max-width: 4rem !important;
} }
.sidebar-tablet {
width: 12rem !important;
min-width: 12rem !important;
max-width: 12rem !important;
position: fixed;
left: 0;
top: 0;
bottom: 0;
z-index: 30;
}
.sidebar-expanded { .sidebar-expanded {
width: 16rem !important; width: 16rem !important;
min-width: 16rem !important; min-width: 16rem !important;
+1 -1
View File
@@ -1,7 +1,7 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send, Upload, XCircle, MessageSquare, Settings } from "lucide-react"; import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send, Upload, XCircle, MessageSquare, Settings } from "lucide-react";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+1 -1
View File
@@ -8,7 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { usePermissions } from "@shared/permissions"; import { usePermissions } from "@shared/permissions";
import { Pagination, usePagination } from "@/components/ui/pagination"; import { Pagination, usePagination } from "@/components/ui/pagination";
+1 -1
View File
@@ -3,7 +3,7 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { safeFormat } from "@/lib/dateUtils"; import { safeFormat } from "@/lib/dateUtils";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { usePermissions } from "@shared/permissions"; import { usePermissions } from "@shared/permissions";
import { Search, Edit, FileText, Settings, Eye, AlertTriangle, X, Check, Trash2, Ban, ChevronLeft, ChevronRight } from "lucide-react"; import { Search, Edit, FileText, Settings, Eye, AlertTriangle, X, Check, Trash2, Ban, ChevronLeft, ChevronRight } from "lucide-react";
+1 -1
View File
@@ -9,7 +9,7 @@ import CreateOrderModal from "@/components/modals/CreateOrderModal";
import CreateDeliveryModal from "@/components/modals/CreateDeliveryModal"; import CreateDeliveryModal from "@/components/modals/CreateDeliveryModal";
import StatsPanel from "@/components/StatsPanel"; import StatsPanel from "@/components/StatsPanel";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { format, startOfMonth, endOfMonth } from "date-fns"; import { format, startOfMonth, endOfMonth } from "date-fns";
+1 -1
View File
@@ -39,7 +39,7 @@ import type { CustomerOrderWithRelations, Group } from "@shared/schema";
import { CustomerOrderForm } from "@/components/CustomerOrderForm"; import { CustomerOrderForm } from "@/components/CustomerOrderForm";
import { CustomerOrderDetails } from "@/components/CustomerOrderDetails"; import { CustomerOrderDetails } from "@/components/CustomerOrderDetails";
import { ConfirmationModal } from "@/components/ConfirmationModal"; import { ConfirmationModal } from "@/components/ConfirmationModal";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { usePermissions } from "@shared/permissions"; import { usePermissions } from "@shared/permissions";
export default function CustomerOrders() { export default function CustomerOrders() {
+1 -1
View File
@@ -1,7 +1,7 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
+1 -1
View File
@@ -6,7 +6,7 @@ import { Badge } from "@/components/ui/badge";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Pagination, usePagination } from "@/components/ui/pagination"; import { Pagination, usePagination } from "@/components/ui/pagination";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { isUnauthorizedError } from "@/lib/authUtils"; import { isUnauthorizedError } from "@/lib/authUtils";
+1 -1
View File
@@ -21,7 +21,7 @@ import { format } from "date-fns";
import { fr } from "date-fns/locale"; import { fr } from "date-fns/locale";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import type { DlcProductWithRelations, InsertDlcProduct } from "@shared/schema"; import type { DlcProductWithRelations, InsertDlcProduct } from "@shared/schema";
+1 -1
View File
@@ -6,7 +6,7 @@ import { Badge } from "@/components/ui/badge";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Pagination, usePagination } from "@/components/ui/pagination"; import { Pagination, usePagination } from "@/components/ui/pagination";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { isUnauthorizedError } from "@/lib/authUtils"; import { isUnauthorizedError } from "@/lib/authUtils";
+1 -1
View File
@@ -8,7 +8,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { format, startOfMonth, endOfMonth, eachWeekOfInterval, eachDayOfInterval, startOfWeek, endOfWeek, isSameMonth, isWithinInterval, startOfYear, endOfYear, getWeek, getMonth } from "date-fns"; import { format, startOfMonth, endOfMonth, eachWeekOfInterval, eachDayOfInterval, startOfWeek, endOfWeek, isSameMonth, isWithinInterval, startOfYear, endOfYear, getWeek, getMonth } from "date-fns";
import { fr } from "date-fns/locale"; import { fr } from "date-fns/locale";
import { safeFormat, safeDate, safeCompareDate } from "@/lib/dateUtils"; import { safeFormat, safeDate, safeCompareDate } from "@/lib/dateUtils";
+1 -1
View File
@@ -11,7 +11,7 @@ import { Textarea } from "@/components/ui/textarea";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { import {
Plus, Plus,
Search, Search,
+1 -1
View File
@@ -1,7 +1,7 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
+1 -1
View File
@@ -1,7 +1,7 @@
import { useState } from "react"; import { useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useStore } from "@/components/Layout"; import { useStore } from "@/contexts/StoreContext";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";