mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Merge pull request #432 from R0m1k3/1.1
Refactor store context and improve responsiveness across the application
This commit is contained in:
29 files changed
+957
-254
No files matched your search
@@ -15,6 +15,10 @@ run = ["npm", "run", "start"]
|
||||
localPort = 5000
|
||||
externalPort = 80
|
||||
|
||||
[[ports]]
|
||||
localPort = 45105
|
||||
externalPort = 3000
|
||||
|
||||
[workflows]
|
||||
runButton = "Project"
|
||||
|
||||
|
||||
+714
-177
File diff suppressed because it is too large.
Load diff
@@ -39,7 +39,7 @@ import {
|
||||
Sparkles
|
||||
} from "lucide-react";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { safeFormat, safeDate } from "@/lib/dateUtils";
|
||||
import {
|
||||
insertAnnouncementSchema,
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
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({
|
||||
orderTaker: z.string().min(1, "Qui a pris la commande est requis"),
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Link, useLocation } from "wouter";
|
||||
import { useAuthSimple } from "@/hooks/useAuthSimple";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useStore } from "./Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { useScreenSize } from "@/hooks/use-screen-size";
|
||||
import { useState } from "react";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
@@ -41,6 +42,7 @@ export default function Sidebar() {
|
||||
const [location] = useLocation();
|
||||
const { sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen } = useStore();
|
||||
const isMobile = useIsMobile();
|
||||
const { screenSize, isMobileOrTablet, isTablet } = useScreenSize();
|
||||
const [showBapModal, setShowBapModal] = useState(false);
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
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
|
||||
const sidebarClasses = isMobile
|
||||
? `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 ${
|
||||
// Classes responsives adaptées avec support tablette
|
||||
const getSidebarClasses = () => {
|
||||
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'
|
||||
}`
|
||||
: `${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
|
||||
console.log('🔧 Sidebar Debug:', {
|
||||
@@ -542,16 +556,32 @@ export default function Sidebar() {
|
||||
return (
|
||||
<Link key={item.path} href={item.path}>
|
||||
<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
|
||||
? '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'
|
||||
}`}
|
||||
title={sidebarCollapsed && !isMobile ? item.label : undefined}
|
||||
onClick={handleMenuClick}
|
||||
>
|
||||
<Icon className={`h-4 w-4 ${sidebarCollapsed && !isMobile ? '' : 'mr-3'}`} />
|
||||
{(!sidebarCollapsed || isMobile) && item.label}
|
||||
<Icon className={`${
|
||||
// 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>
|
||||
</Link>
|
||||
);
|
||||
@@ -582,15 +612,29 @@ export default function Sidebar() {
|
||||
return (
|
||||
<div key={item.path}>
|
||||
<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`}
|
||||
title={sidebarCollapsed ? item.label : undefined}
|
||||
className={`flex items-center cursor-pointer transition-colors hover:bg-gray-100 text-gray-700 ${
|
||||
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={() => {
|
||||
setShowBapModal(true);
|
||||
if (isMobile) setMobileMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
|
||||
{!sidebarCollapsed && item.label}
|
||||
<Icon className={`${
|
||||
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>
|
||||
);
|
||||
@@ -599,16 +643,30 @@ export default function Sidebar() {
|
||||
return (
|
||||
<Link key={item.path} href={item.path}>
|
||||
<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
|
||||
? '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'
|
||||
}`}
|
||||
title={sidebarCollapsed ? item.label : undefined}
|
||||
title={sidebarCollapsed && !isMobile ? item.label : undefined}
|
||||
onClick={() => console.log(`Navigating to admin: ${item.path}`)}
|
||||
>
|
||||
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
|
||||
{!sidebarCollapsed && item.label}
|
||||
<Icon className={`${
|
||||
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>
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { TrendingUp, Package, Truck, Clock } from "lucide-react";
|
||||
import { useStore } from "./Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
|
||||
interface StatsPanelProps {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { isUnauthorizedError } from "@/lib/authUtils";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { isUnauthorizedError } from "@/lib/authUtils";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { format, addDays, isAfter, isBefore } from "date-fns";
|
||||
import { fr } from "date-fns/locale";
|
||||
import { useLocation } from "wouter";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { Task } from "@shared/schema";
|
||||
|
||||
type TaskWithRelations = Task & {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,17 @@
|
||||
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 {
|
||||
width: 16rem !important;
|
||||
min-width: 16rem !important;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
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 { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { usePermissions } from "@shared/permissions";
|
||||
import { Pagination, usePagination } from "@/components/ui/pagination";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { safeFormat } from "@/lib/dateUtils";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { usePermissions } from "@shared/permissions";
|
||||
import { Search, Edit, FileText, Settings, Eye, AlertTriangle, X, Check, Trash2, Ban, ChevronLeft, ChevronRight } from "lucide-react";
|
||||
|
||||
@@ -9,7 +9,7 @@ import CreateOrderModal from "@/components/modals/CreateOrderModal";
|
||||
import CreateDeliveryModal from "@/components/modals/CreateDeliveryModal";
|
||||
import StatsPanel from "@/components/StatsPanel";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { format, startOfMonth, endOfMonth } from "date-fns";
|
||||
|
||||
@@ -39,7 +39,7 @@ import type { CustomerOrderWithRelations, Group } from "@shared/schema";
|
||||
import { CustomerOrderForm } from "@/components/CustomerOrderForm";
|
||||
import { CustomerOrderDetails } from "@/components/CustomerOrderDetails";
|
||||
import { ConfirmationModal } from "@/components/ConfirmationModal";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { usePermissions } from "@shared/permissions";
|
||||
|
||||
export default function CustomerOrders() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
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 { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Pagination, usePagination } from "@/components/ui/pagination";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { isUnauthorizedError } from "@/lib/authUtils";
|
||||
|
||||
@@ -21,7 +21,7 @@ import { format } from "date-fns";
|
||||
import { fr } from "date-fns/locale";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import type { DlcProductWithRelations, InsertDlcProduct } from "@shared/schema";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Pagination, usePagination } from "@/components/ui/pagination";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { isUnauthorizedError } from "@/lib/authUtils";
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
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 { fr } from "date-fns/locale";
|
||||
import { safeFormat, safeDate, safeCompareDate } from "@/lib/dateUtils";
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import {
|
||||
Plus,
|
||||
Search,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
Reference in new issue
Block a user