mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-20 05:28:22 +02:00
Restored to '479577c4ff1b136dd2b1cea9dffd6c0fc0018d59'
Replit-Restored-To: 479577c4ff
This commit is contained in:
1 parent
1346c9b34c
commit
a4a637d415
13 files changed
+128
-347
No files matched your search
Binary file not shown.
|
Before Width: | Height: | Size: 161 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 333 KiB |
@@ -99,7 +99,7 @@ export function CustomerOrderForm({
|
|||||||
quantity: order?.quantity || 1,
|
quantity: order?.quantity || 1,
|
||||||
supplierId: order?.supplierId || undefined,
|
supplierId: order?.supplierId || undefined,
|
||||||
status: "En attente de Commande", // Statut fixe
|
status: "En attente de Commande", // Statut fixe
|
||||||
deposit: Number(order?.deposit) || 0,
|
deposit: order?.deposit || 0,
|
||||||
isPromotionalPrice: order?.isPromotionalPrice || false,
|
isPromotionalPrice: order?.isPromotionalPrice || false,
|
||||||
customerNotified: order?.customerNotified || false,
|
customerNotified: order?.customerNotified || false,
|
||||||
groupId: getDefaultGroupId(), // ✅ PRIORITÉ CORRECTE APPLIQUÉE
|
groupId: getDefaultGroupId(), // ✅ PRIORITÉ CORRECTE APPLIQUÉE
|
||||||
@@ -125,7 +125,7 @@ export function CustomerOrderForm({
|
|||||||
selectedStoreId,
|
selectedStoreId,
|
||||||
userHasGroups: !!user?.userGroups,
|
userHasGroups: !!user?.userGroups,
|
||||||
userGroupsLength: user?.userGroups?.length,
|
userGroupsLength: user?.userGroups?.length,
|
||||||
userGroups: user?.userGroups?.map((ug: any) => ({
|
userGroups: user?.userGroups?.map(ug => ({
|
||||||
groupId: ug.groupId || ug.group?.id,
|
groupId: ug.groupId || ug.group?.id,
|
||||||
groupName: ug.group?.name,
|
groupName: ug.group?.name,
|
||||||
fullGroupData: ug
|
fullGroupData: ug
|
||||||
@@ -156,7 +156,7 @@ export function CustomerOrderForm({
|
|||||||
contactNumber: data.contactNumber || '',
|
contactNumber: data.contactNumber || '',
|
||||||
productName: data.productName || '',
|
productName: data.productName || '',
|
||||||
quantity: data.quantity,
|
quantity: data.quantity,
|
||||||
groupId: typeof groupId === 'number' ? groupId : parseInt(groupId?.toString() || '1'),
|
groupId: typeof groupId === 'number' ? groupId : parseInt(groupId.toString()),
|
||||||
isPickup: false,
|
isPickup: false,
|
||||||
notes: data.notes,
|
notes: data.notes,
|
||||||
deposit: data.deposit || 0,
|
deposit: data.deposit || 0,
|
||||||
@@ -173,7 +173,7 @@ export function CustomerOrderForm({
|
|||||||
// Get available groups for the user
|
// Get available groups for the user
|
||||||
const availableGroups = user?.role === 'admin'
|
const availableGroups = user?.role === 'admin'
|
||||||
? groups
|
? groups
|
||||||
: user?.userGroups?.map((ug: any) => ug.group) || [];
|
: user?.userGroups?.map(ug => ug.group) || [];
|
||||||
|
|
||||||
// Auto-select group for users respecting admin store selection
|
// Auto-select group for users respecting admin store selection
|
||||||
const currentGroupId = form.getValues('groupId');
|
const currentGroupId = form.getValues('groupId');
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import { ReactNode, useState, createContext, useContext } 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, X } from "lucide-react";
|
import { LogOut, Store } from "lucide-react";
|
||||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||||
import { useResponsive } from "@/hooks/useResponsive";
|
|
||||||
import Sidebar from "./Sidebar";
|
import Sidebar from "./Sidebar";
|
||||||
import type { Group } from "@shared/schema";
|
import type { Group } from "@shared/schema";
|
||||||
|
|
||||||
@@ -14,8 +13,6 @@ interface StoreContextType {
|
|||||||
stores: Group[];
|
stores: Group[];
|
||||||
sidebarCollapsed: boolean;
|
sidebarCollapsed: boolean;
|
||||||
setSidebarCollapsed: (collapsed: boolean) => void;
|
setSidebarCollapsed: (collapsed: boolean) => void;
|
||||||
mobileMenuOpen: boolean;
|
|
||||||
setMobileMenuOpen: (open: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const StoreContext = createContext<StoreContextType | undefined>(undefined);
|
const StoreContext = createContext<StoreContextType | undefined>(undefined);
|
||||||
@@ -35,8 +32,6 @@ interface LayoutProps {
|
|||||||
export default function Layout({ children }: LayoutProps) {
|
export default function Layout({ children }: LayoutProps) {
|
||||||
const { user } = useAuthUnified();
|
const { user } = useAuthUnified();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { isMobile, isTablet, isDesktop } = useResponsive();
|
|
||||||
|
|
||||||
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
|
||||||
const saved = localStorage.getItem('selectedStoreId');
|
const saved = localStorage.getItem('selectedStoreId');
|
||||||
@@ -48,12 +43,9 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState<boolean>(() => {
|
const [sidebarCollapsed, setSidebarCollapsed] = useState<boolean>(() => {
|
||||||
// Restaurer l'état de la sidebar depuis localStorage
|
// Restaurer l'état de la sidebar depuis localStorage
|
||||||
const saved = localStorage.getItem('sidebarCollapsed');
|
const saved = localStorage.getItem('sidebarCollapsed');
|
||||||
// Sur mobile, toujours collapsé par défaut
|
return saved ? JSON.parse(saved) : false;
|
||||||
return saved ? JSON.parse(saved) : isMobile;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const { data: stores = [] } = useQuery<Group[]>({
|
const { data: stores = [] } = useQuery<Group[]>({
|
||||||
queryKey: ['/api/groups'],
|
queryKey: ['/api/groups'],
|
||||||
enabled: !!user,
|
enabled: !!user,
|
||||||
@@ -64,56 +56,20 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StoreContext.Provider value={{
|
<StoreContext.Provider value={{ selectedStoreId, setSelectedStoreId, stores, sidebarCollapsed, setSidebarCollapsed }}>
|
||||||
selectedStoreId,
|
<div className="flex h-screen overflow-hidden bg-gray-50">
|
||||||
setSelectedStoreId,
|
<Sidebar />
|
||||||
stores,
|
<main className="flex-1 flex flex-col overflow-hidden">
|
||||||
sidebarCollapsed,
|
|
||||||
setSidebarCollapsed,
|
|
||||||
mobileMenuOpen,
|
|
||||||
setMobileMenuOpen
|
|
||||||
}}>
|
|
||||||
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
|
|
||||||
{/* Mobile Sidebar Overlay */}
|
|
||||||
{isMobile && mobileMenuOpen && (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 bg-black/50 z-40"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
/>
|
|
||||||
<div className="fixed inset-y-0 left-0 z-50 w-64 bg-white dark:bg-gray-800">
|
|
||||||
<Sidebar />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Desktop Sidebar */}
|
|
||||||
{!isMobile && <Sidebar />}
|
|
||||||
|
|
||||||
<main className={`flex-1 flex flex-col min-h-0 ${
|
|
||||||
isMobile ? 'ml-0' : sidebarCollapsed ? 'ml-16' : 'ml-64'
|
|
||||||
}`}>
|
|
||||||
{/* Header with store selector for admin */}
|
{/* Header with store selector for admin */}
|
||||||
<header className="h-16 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between px-6">
|
<header className="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{/* Mobile menu button */}
|
<h1 className="text-lg font-medium text-gray-800">LogiFlow</h1>
|
||||||
{isMobile && (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
||||||
className="h-8 w-8 p-0"
|
|
||||||
>
|
|
||||||
{mobileMenuOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<h1 className="text-lg sm:text-xl font-semibold text-gray-800 dark:text-gray-200">LogiFlow</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Store selector for admin - moved to top right */}
|
{/* Store selector for admin - moved to top right */}
|
||||||
{user?.role === 'admin' && stores.length > 0 && (
|
{user?.role === 'admin' && stores.length > 0 && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Store className="h-4 w-4 text-gray-500 dark:text-gray-400" />
|
<Store className="h-4 w-4 text-gray-500" />
|
||||||
<Select
|
<Select
|
||||||
value={selectedStoreId?.toString() || "all"}
|
value={selectedStoreId?.toString() || "all"}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
@@ -155,7 +111,7 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div
|
<div
|
||||||
className="w-3 h-3"
|
className="w-3 h-3"
|
||||||
style={{ backgroundColor: store.color || '#6B7280' }}
|
style={{ backgroundColor: store.color }}
|
||||||
/>
|
/>
|
||||||
<span>{store.name}</span>
|
<span>{store.name}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,19 +123,10 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Main content area */}
|
<div className="flex-1 overflow-auto bg-gray-50 p-6">
|
||||||
<div className="flex-1 bg-gray-50 dark:bg-gray-900 overflow-hidden">
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Mobile menu close overlay */}
|
|
||||||
{isMobile && mobileMenuOpen && (
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 bg-black/20 z-30"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</StoreContext.Provider>
|
</StoreContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Link, useLocation } from "wouter";
|
import { Link, useLocation } from "wouter";
|
||||||
import { useAuthSimple } from "@/hooks/useAuthSimple";
|
import { useAuthSimple } from "@/hooks/useAuthSimple";
|
||||||
import { useResponsive } from "@/hooks/useResponsive";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useStore } from "./Layout";
|
import { useStore } from "./Layout";
|
||||||
import {
|
import {
|
||||||
@@ -26,8 +25,7 @@ import {
|
|||||||
export default function Sidebar() {
|
export default function Sidebar() {
|
||||||
const { user, isLoading, error } = useAuthSimple();
|
const { user, isLoading, error } = useAuthSimple();
|
||||||
const [location] = useLocation();
|
const [location] = useLocation();
|
||||||
const { isMobile, isTablet, isDesktop } = useResponsive();
|
const { sidebarCollapsed, setSidebarCollapsed } = useStore();
|
||||||
const { sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen } = useStore();
|
|
||||||
|
|
||||||
// Debug logging pour production
|
// Debug logging pour production
|
||||||
console.log('Sidebar - User:', user);
|
console.log('Sidebar - User:', user);
|
||||||
@@ -50,20 +48,9 @@ export default function Sidebar() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const toggleSidebar = () => {
|
const toggleSidebar = () => {
|
||||||
if (isMobile) {
|
const newCollapsed = !sidebarCollapsed;
|
||||||
setMobileMenuOpen(!mobileMenuOpen);
|
setSidebarCollapsed(newCollapsed);
|
||||||
} else {
|
localStorage.setItem('sidebarCollapsed', JSON.stringify(newCollapsed));
|
||||||
const newCollapsed = !sidebarCollapsed;
|
|
||||||
setSidebarCollapsed(newCollapsed);
|
|
||||||
localStorage.setItem('sidebarCollapsed', JSON.stringify(newCollapsed));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Close mobile menu when clicking on a link
|
|
||||||
const handleLinkClick = () => {
|
|
||||||
if (isMobile && mobileMenuOpen) {
|
|
||||||
setMobileMenuOpen(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getInitials = (firstName?: string, lastName?: string) => {
|
const getInitials = (firstName?: string, lastName?: string) => {
|
||||||
@@ -181,13 +168,10 @@ export default function Sidebar() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sur mobile, toujours afficher en mode collapsed quand fermé
|
|
||||||
const isCollapsed = isMobile ? false : sidebarCollapsed;
|
|
||||||
|
|
||||||
// Si l'utilisateur n'est pas encore chargé, afficher un état de chargement
|
// Si l'utilisateur n'est pas encore chargé, afficher un état de chargement
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<aside className={`${isCollapsed ? 'w-16' : 'w-64'} bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
|
<aside className={`${sidebarCollapsed ? 'w-16' : 'w-64'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
|
||||||
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
|
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
|
||||||
{!sidebarCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
@@ -223,7 +207,7 @@ export default function Sidebar() {
|
|||||||
// Si l'utilisateur n'est pas authentifié, afficher seulement le logo
|
// Si l'utilisateur n'est pas authentifié, afficher seulement le logo
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return (
|
return (
|
||||||
<aside className={`${isCollapsed ? 'w-16' : 'w-64'} bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
|
<aside className={`${sidebarCollapsed ? 'w-16' : 'w-64'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
|
||||||
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
|
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
|
||||||
{!sidebarCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
@@ -259,38 +243,36 @@ export default function Sidebar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className={`${isCollapsed ? 'w-16' : 'w-64'} bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 flex flex-col shadow-lg transition-all duration-300 ease-in-out h-screen`}>
|
<aside className={`${sidebarCollapsed ? 'w-16' : 'w-64'} bg-white border-r border-gray-200 flex flex-col shadow-lg transition-all duration-300 ease-in-out`}>
|
||||||
{/* Logo et bouton de collapse */}
|
{/* Logo et bouton de collapse */}
|
||||||
<div className="h-16 flex items-center justify-between border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-4">
|
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
|
||||||
{!isCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<Store className="h-6 w-6 text-blue-600 dark:text-blue-400" />
|
<Store className="h-6 w-6 text-blue-600" />
|
||||||
<span className="text-lg font-bold text-gray-900 dark:text-gray-100">LogiFlow</span>
|
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isCollapsed && (
|
{sidebarCollapsed && (
|
||||||
<div className="flex items-center justify-center w-full">
|
<div className="flex items-center justify-center w-full">
|
||||||
<Store className="h-6 w-6 text-blue-600 dark:text-blue-400" />
|
<Store className="h-6 w-6 text-blue-600" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isMobile && (
|
<Button
|
||||||
<Button
|
variant="ghost"
|
||||||
variant="ghost"
|
size="sm"
|
||||||
size="sm"
|
onClick={toggleSidebar}
|
||||||
onClick={toggleSidebar}
|
className="h-8 w-8 p-0 hover:bg-gray-100"
|
||||||
className="h-8 w-8 p-0 hover:bg-gray-100 dark:hover:bg-gray-700"
|
>
|
||||||
>
|
{sidebarCollapsed ? (
|
||||||
{isCollapsed ? (
|
<ChevronRight className="h-4 w-4" />
|
||||||
<ChevronRight className="h-4 w-4" />
|
) : (
|
||||||
) : (
|
<ChevronLeft className="h-4 w-4" />
|
||||||
<ChevronLeft className="h-4 w-4" />
|
)}
|
||||||
)}
|
</Button>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<nav className="flex-1 py-4 px-4 overflow-y-auto">
|
<nav className="flex-1 py-4 px-3">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{menuItems.map((item) => {
|
{menuItems.map((item) => {
|
||||||
const hasRolePermission = hasPermission(item.roles);
|
const hasRolePermission = hasPermission(item.roles);
|
||||||
@@ -308,16 +290,16 @@ 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 ${isCollapsed ? 'px-3 py-3 justify-center' : 'px-3 py-2'} text-sm font-medium transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 ${
|
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 ${
|
||||||
active
|
active
|
||||||
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-r-2 border-blue-600 dark:border-blue-400'
|
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700'
|
||||||
: 'text-gray-700 dark:text-gray-300'
|
: 'text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
title={isCollapsed ? item.label : undefined}
|
title={sidebarCollapsed ? item.label : undefined}
|
||||||
onClick={handleLinkClick}
|
onClick={() => console.log(`Navigating to: ${item.path}`)}
|
||||||
>
|
>
|
||||||
<Icon className={`${isCollapsed ? 'h-5 w-5' : 'h-4 w-4 mr-3'}`} />
|
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
|
||||||
{!isCollapsed && item.label}
|
{!sidebarCollapsed && item.label}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@@ -327,14 +309,14 @@ export default function Sidebar() {
|
|||||||
{/* Management Section */}
|
{/* Management Section */}
|
||||||
{managementItems.some(item => hasPermission(item.roles)) && (
|
{managementItems.some(item => hasPermission(item.roles)) && (
|
||||||
<>
|
<>
|
||||||
{!isCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
<div className="mt-6 mb-2">
|
<div className="mt-6 mb-2">
|
||||||
<h3 className="px-3 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
<h3 className="px-3 text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Gestion
|
Gestion
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isCollapsed && <div className="mt-4 mb-2 border-t border-gray-200 dark:border-gray-700"></div>}
|
{sidebarCollapsed && <div className="mt-4 mb-2 border-t border-gray-200"></div>}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{managementItems.map((item) => {
|
{managementItems.map((item) => {
|
||||||
if (!hasPermission(item.roles)) return null;
|
if (!hasPermission(item.roles)) return null;
|
||||||
@@ -345,16 +327,16 @@ 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 ${isCollapsed ? 'px-3 py-3 justify-center' : 'px-3 py-2'} text-sm font-medium transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 ${
|
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 ${
|
||||||
active
|
active
|
||||||
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-r-2 border-blue-600 dark:border-blue-400'
|
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700'
|
||||||
: 'text-gray-700 dark:text-gray-300'
|
: 'text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
title={isCollapsed ? item.label : undefined}
|
title={sidebarCollapsed ? item.label : undefined}
|
||||||
onClick={handleLinkClick}
|
onClick={() => console.log(`Navigating to management: ${item.path}`)}
|
||||||
>
|
>
|
||||||
<Icon className={`${isCollapsed ? 'h-5 w-5' : 'h-4 w-4 mr-3'}`} />
|
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
|
||||||
{!isCollapsed && item.label}
|
{!sidebarCollapsed && item.label}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@@ -366,10 +348,10 @@ export default function Sidebar() {
|
|||||||
|
|
||||||
{/* Administration Section */}
|
{/* Administration Section */}
|
||||||
{adminItems.some(item => hasPermission(item.roles)) && (
|
{adminItems.some(item => hasPermission(item.roles)) && (
|
||||||
<div className="border-t border-gray-200 dark:border-gray-700 py-4 px-3">
|
<div className="border-t border-gray-200 py-4 px-3">
|
||||||
{!isCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<h3 className="px-3 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
<h3 className="px-3 text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Administration
|
Administration
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -384,16 +366,16 @@ 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 ${isCollapsed ? 'px-3 py-3 justify-center' : 'px-3 py-2'} text-sm font-medium transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 ${
|
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 ${
|
||||||
active
|
active
|
||||||
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-r-2 border-blue-600 dark:border-blue-400'
|
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700'
|
||||||
: 'text-gray-700 dark:text-gray-300'
|
: 'text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
title={isCollapsed ? item.label : undefined}
|
title={sidebarCollapsed ? item.label : undefined}
|
||||||
onClick={handleLinkClick}
|
onClick={() => console.log(`Navigating to admin: ${item.path}`)}
|
||||||
>
|
>
|
||||||
<Icon className={`${isCollapsed ? 'h-5 w-5' : 'h-4 w-4 mr-3'}`} />
|
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
|
||||||
{!isCollapsed && item.label}
|
{!sidebarCollapsed && item.label}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@@ -403,20 +385,20 @@ export default function Sidebar() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* User Profile & Logout */}
|
{/* User Profile & Logout */}
|
||||||
<div className="border-t border-gray-200 dark:border-gray-700 p-4">
|
<div className="border-t border-gray-200 p-4">
|
||||||
{!isCollapsed ? (
|
{!sidebarCollapsed ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center space-x-3 mb-3">
|
<div className="flex items-center space-x-3 mb-3">
|
||||||
<div className="h-8 w-8 bg-gray-100 dark:bg-gray-700 flex items-center justify-center rounded-full">
|
<div className="h-8 w-8 bg-gray-100 flex items-center justify-center">
|
||||||
<span className="text-xs font-medium text-gray-700 dark:text-gray-300">
|
<span className="text-xs font-medium text-gray-700">
|
||||||
{getInitials(user?.firstName, user?.lastName)}
|
{getInitials(user?.firstName, user?.lastName)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
|
<p className="text-sm font-medium text-gray-900 truncate">
|
||||||
{user?.firstName || user?.lastName ? `${user?.firstName || ''} ${user?.lastName || ''}`.trim() : user?.username}
|
{user?.firstName || user?.lastName ? `${user?.firstName || ''} ${user?.lastName || ''}`.trim() : user?.username}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
<p className="text-xs text-gray-500 truncate">
|
||||||
{user?.role === 'admin' ? 'Administrateur' :
|
{user?.role === 'admin' ? 'Administrateur' :
|
||||||
user?.role === 'manager' ? 'Manager' :
|
user?.role === 'manager' ? 'Manager' :
|
||||||
user?.role === 'directeur' ? 'Directeur' : 'Employé'}
|
user?.role === 'directeur' ? 'Directeur' : 'Employé'}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { useState, useMemo, useEffect } from 'react';
|
|
||||||
|
|
||||||
export function usePagination<T>(data: T[], initialItemsPerPage: number = 10) {
|
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
|
||||||
const [itemsPerPage, setItemsPerPage] = useState(initialItemsPerPage);
|
|
||||||
|
|
||||||
const totalItems = data.length;
|
|
||||||
const totalPages = Math.ceil(totalItems / itemsPerPage);
|
|
||||||
|
|
||||||
// Reset to first page when data changes
|
|
||||||
useEffect(() => {
|
|
||||||
setCurrentPage(1);
|
|
||||||
}, [data.length, itemsPerPage]);
|
|
||||||
|
|
||||||
const paginatedData = useMemo(() => {
|
|
||||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
|
||||||
const endIndex = startIndex + itemsPerPage;
|
|
||||||
return data.slice(startIndex, endIndex);
|
|
||||||
}, [data, currentPage, itemsPerPage]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
currentPage,
|
|
||||||
setCurrentPage,
|
|
||||||
itemsPerPage,
|
|
||||||
setItemsPerPage,
|
|
||||||
totalPages,
|
|
||||||
totalItems,
|
|
||||||
paginatedData,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { useState, useEffect } from "react";
|
|
||||||
|
|
||||||
interface ResponsiveState {
|
|
||||||
isMobile: boolean;
|
|
||||||
isTablet: boolean;
|
|
||||||
isDesktop: boolean;
|
|
||||||
screenWidth: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BREAKPOINTS = {
|
|
||||||
mobile: 768,
|
|
||||||
tablet: 1024,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export function useResponsive(): ResponsiveState {
|
|
||||||
const [state, setState] = useState<ResponsiveState>(() => {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
return {
|
|
||||||
isMobile: false,
|
|
||||||
isTablet: false,
|
|
||||||
isDesktop: true,
|
|
||||||
screenWidth: 1920,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = window.innerWidth;
|
|
||||||
return {
|
|
||||||
isMobile: width < BREAKPOINTS.mobile,
|
|
||||||
isTablet: width >= BREAKPOINTS.mobile && width < BREAKPOINTS.tablet,
|
|
||||||
isDesktop: width >= BREAKPOINTS.tablet,
|
|
||||||
screenWidth: width,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleResize = () => {
|
|
||||||
const width = window.innerWidth;
|
|
||||||
setState({
|
|
||||||
isMobile: width < BREAKPOINTS.mobile,
|
|
||||||
isTablet: width >= BREAKPOINTS.mobile && width < BREAKPOINTS.tablet,
|
|
||||||
isDesktop: width >= BREAKPOINTS.tablet,
|
|
||||||
screenWidth: width,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('resize', handleResize);
|
|
||||||
return () => window.removeEventListener('resize', handleResize);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hook spécialisé pour la navigation mobile
|
|
||||||
export function useIsMobileNavigation() {
|
|
||||||
const { isMobile } = useResponsive();
|
|
||||||
return isMobile;
|
|
||||||
}
|
|
||||||
@@ -125,57 +125,6 @@
|
|||||||
.hover\:bg-accent:hover {
|
.hover\:bg-accent:hover {
|
||||||
background-color: var(--accent);
|
background-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive utilities - Optimisé pour grand écran */
|
|
||||||
.container-responsive {
|
|
||||||
@apply w-full mx-auto px-4 sm:px-6 lg:px-8 xl:px-12;
|
|
||||||
max-width: none; /* Utilise toute la largeur disponible */
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-responsive {
|
|
||||||
@apply grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-responsive {
|
|
||||||
@apply bg-card border border-border rounded-xl shadow-sm hover:shadow-lg transition-all p-6 sm:p-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-responsive-sm {
|
|
||||||
@apply text-base sm:text-lg lg:text-xl;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-responsive-lg {
|
|
||||||
@apply text-xl sm:text-2xl lg:text-3xl xl:text-4xl;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-width {
|
|
||||||
@apply w-16 lg:w-72 xl:w-80;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-collapsed {
|
|
||||||
@apply w-16;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
@apply flex-1 flex flex-col overflow-hidden ml-16 lg:ml-72 xl:ml-80;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content-expanded {
|
|
||||||
@apply ml-16;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Utilitaires pour grand écran */
|
|
||||||
.desktop-padding {
|
|
||||||
@apply p-8 xl:p-12;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktop-text {
|
|
||||||
@apply text-lg xl:text-xl;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktop-spacing {
|
|
||||||
@apply space-y-8 xl:space-y-12;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Supprimer le contour orange de la date du jour dans le calendrier */
|
/* Supprimer le contour orange de la date du jour dans le calendrier */
|
||||||
|
|||||||
@@ -674,8 +674,7 @@ export default function CustomerOrders() {
|
|||||||
} = usePagination(sortedOrders, 10);
|
} = usePagination(sortedOrders, 10);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 min-h-0 flex flex-col">
|
<div className="p-6 space-y-6">
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -753,7 +752,7 @@ export default function CustomerOrders() {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div>Chargement...</div>
|
<div>Chargement...</div>
|
||||||
) : (
|
) : (
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Client</TableHead>
|
<TableHead>Client</TableHead>
|
||||||
@@ -985,6 +984,5 @@ export default function CustomerOrders() {
|
|||||||
isLoading={deleteMutation.isPending}
|
isLoading={deleteMutation.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ export default function Dashboard() {
|
|||||||
const deliveredThisMonth = Array.isArray(allDeliveries) ? allDeliveries.filter((delivery: any) => {
|
const deliveredThisMonth = Array.isArray(allDeliveries) ? allDeliveries.filter((delivery: any) => {
|
||||||
const deliveryDate = safeDate(delivery.deliveredDate || delivery.createdAt);
|
const deliveryDate = safeDate(delivery.deliveredDate || delivery.createdAt);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return deliveryDate && deliveryDate.getMonth() === now.getMonth() &&
|
return deliveryDate.getMonth() === now.getMonth() &&
|
||||||
deliveryDate.getFullYear() === now.getFullYear() &&
|
deliveryDate.getFullYear() === now.getFullYear() &&
|
||||||
delivery.status === 'delivered';
|
delivery.status === 'delivered';
|
||||||
}).length : 0;
|
}).length : 0;
|
||||||
@@ -148,7 +148,7 @@ export default function Dashboard() {
|
|||||||
if (delivery.status === 'delivered' && delivery.unit === 'palettes') {
|
if (delivery.status === 'delivered' && delivery.unit === 'palettes') {
|
||||||
const deliveryDate = safeDate(delivery.deliveredDate || delivery.createdAt);
|
const deliveryDate = safeDate(delivery.deliveredDate || delivery.createdAt);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (deliveryDate && deliveryDate.getMonth() === now.getMonth() && deliveryDate.getFullYear() === now.getFullYear()) {
|
if (deliveryDate.getMonth() === now.getMonth() && deliveryDate.getFullYear() === now.getFullYear()) {
|
||||||
return total + (delivery.quantity || 0);
|
return total + (delivery.quantity || 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,8 +218,7 @@ export default function Dashboard() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 min-h-0 flex flex-col">
|
<div className="flex-1 space-y-6 p-6">
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -404,7 +403,7 @@ export default function Dashboard() {
|
|||||||
.slice(0, 3)
|
.slice(0, 3)
|
||||||
.map((publicity: any) => {
|
.map((publicity: any) => {
|
||||||
const participatingStores = publicity.participations || [];
|
const participatingStores = publicity.participations || [];
|
||||||
const isCurrentStoreParticipating = selectedStoreId && participatingStores.some((p: any) => p.groupId === parseInt(selectedStoreId.toString()));
|
const isCurrentStoreParticipating = selectedStoreId && participatingStores.some((p: any) => p.groupId === parseInt(selectedStoreId));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={publicity.id} className="p-4 bg-gray-50 hover:bg-gray-100 transition-colors border-l-3 border-purple-500 space-y-2">
|
<div key={publicity.id} className="p-4 bg-gray-50 hover:bg-gray-100 transition-colors border-l-3 border-purple-500 space-y-2">
|
||||||
@@ -437,7 +436,7 @@ export default function Dashboard() {
|
|||||||
<Badge
|
<Badge
|
||||||
key={participation.groupId}
|
key={participation.groupId}
|
||||||
className={`text-xs ${
|
className={`text-xs ${
|
||||||
selectedStoreId && participation.groupId === parseInt(selectedStoreId.toString())
|
selectedStoreId && participation.groupId === parseInt(selectedStoreId)
|
||||||
? 'bg-green-100 text-green-800 border border-green-300'
|
? 'bg-green-100 text-green-800 border border-green-300'
|
||||||
: 'bg-gray-100 text-gray-700'
|
: 'bg-gray-100 text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
@@ -566,6 +565,5 @@ export default function Dashboard() {
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Badge } from "@/components/ui/badge";
|
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 } from "@/components/ui/pagination";
|
import { Pagination, usePagination } from "@/components/ui/pagination";
|
||||||
import { usePagination } from "@/hooks/usePagination";
|
|
||||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||||
import { useStore } from "@/components/Layout";
|
import { useStore } from "@/components/Layout";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
@@ -30,7 +29,6 @@ import CreateDeliveryModal from "@/components/modals/CreateDeliveryModal";
|
|||||||
import EditDeliveryModal from "@/components/modals/EditDeliveryModal";
|
import EditDeliveryModal from "@/components/modals/EditDeliveryModal";
|
||||||
import OrderDetailModal from "@/components/modals/OrderDetailModal";
|
import OrderDetailModal from "@/components/modals/OrderDetailModal";
|
||||||
import ConfirmDeleteModal from "@/components/modals/ConfirmDeleteModal";
|
import ConfirmDeleteModal from "@/components/modals/ConfirmDeleteModal";
|
||||||
import { usePermissions } from "@shared/permissions";
|
|
||||||
import type { DeliveryWithRelations } from "@shared/schema";
|
import type { DeliveryWithRelations } from "@shared/schema";
|
||||||
|
|
||||||
export default function Deliveries() {
|
export default function Deliveries() {
|
||||||
@@ -38,7 +36,6 @@ export default function Deliveries() {
|
|||||||
const { selectedStoreId } = useStore();
|
const { selectedStoreId } = useStore();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const permissions = usePermissions(user?.role);
|
|
||||||
|
|
||||||
// Redirection pour les employés
|
// Redirection pour les employés
|
||||||
if (user?.role === 'employee') {
|
if (user?.role === 'employee') {
|
||||||
@@ -256,8 +253,7 @@ export default function Deliveries() {
|
|||||||
const canValidate = permissions.canValidate('deliveries');
|
const canValidate = permissions.canValidate('deliveries');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 min-h-0 flex flex-col">
|
<div className="p-6 space-y-6">
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -340,10 +336,11 @@ export default function Deliveries() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white border border-gray-200 shadow-lg overflow-hidden rounded-xl">
|
<div className="flex-1 overflow-y-auto p-6">
|
||||||
<div className="overflow-x-auto">
|
<div className="bg-white border border-gray-200 shadow-lg overflow-hidden">
|
||||||
<table className="w-full">
|
<div className="overflow-x-auto">
|
||||||
<thead className="bg-gray-50 border-b border-gray-200">
|
<table className="w-full">
|
||||||
|
<thead className="bg-gray-50 border-b border-gray-200">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Fournisseur
|
Fournisseur
|
||||||
@@ -370,8 +367,8 @@ export default function Deliveries() {
|
|||||||
Actions
|
Actions
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{paginatedDeliveries.map((delivery) => (
|
{paginatedDeliveries.map((delivery) => (
|
||||||
<tr key={delivery.id} className="hover:bg-gray-50">
|
<tr key={delivery.id} className="hover:bg-gray-50">
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
@@ -469,20 +466,21 @@ export default function Deliveries() {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
totalPages={totalPages}
|
totalPages={totalPages}
|
||||||
totalItems={totalItems}
|
totalItems={totalItems}
|
||||||
itemsPerPage={itemsPerPage}
|
itemsPerPage={itemsPerPage}
|
||||||
onPageChange={setCurrentPage}
|
onPageChange={setCurrentPage}
|
||||||
onItemsPerPageChange={setItemsPerPage}
|
onItemsPerPageChange={setItemsPerPage}
|
||||||
className="mt-4 p-4 border-t border-gray-200"
|
className="mt-4 p-4 border-t border-gray-200"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -525,7 +523,6 @@ export default function Deliveries() {
|
|||||||
itemName={deliveryToDelete ? `${deliveryToDelete.supplier?.name} - ${safeFormat(deliveryToDelete.scheduledDate, 'dd/MM/yyyy')}` : undefined}
|
itemName={deliveryToDelete ? `${deliveryToDelete.supplier?.name} - ${safeFormat(deliveryToDelete.scheduledDate, 'dd/MM/yyyy')}` : undefined}
|
||||||
isLoading={deleteMutation.isPending}
|
isLoading={deleteMutation.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+24
-27
@@ -4,8 +4,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Badge } from "@/components/ui/badge";
|
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 } from "@/components/ui/pagination";
|
import { Pagination, usePagination } from "@/components/ui/pagination";
|
||||||
import { usePagination } from "@/hooks/usePagination";
|
|
||||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||||
import { useStore } from "@/components/Layout";
|
import { useStore } from "@/components/Layout";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
@@ -28,7 +27,6 @@ import CreateOrderModal from "@/components/modals/CreateOrderModal";
|
|||||||
import EditOrderModal from "@/components/modals/EditOrderModal";
|
import EditOrderModal from "@/components/modals/EditOrderModal";
|
||||||
import OrderDetailModal from "@/components/modals/OrderDetailModal";
|
import OrderDetailModal from "@/components/modals/OrderDetailModal";
|
||||||
import ConfirmDeleteModal from "@/components/modals/ConfirmDeleteModal";
|
import ConfirmDeleteModal from "@/components/modals/ConfirmDeleteModal";
|
||||||
import { usePermissions } from "@shared/permissions";
|
|
||||||
import type { OrderWithRelations } from "@shared/schema";
|
import type { OrderWithRelations } from "@shared/schema";
|
||||||
|
|
||||||
export default function Orders() {
|
export default function Orders() {
|
||||||
@@ -36,7 +34,6 @@ export default function Orders() {
|
|||||||
const { selectedStoreId } = useStore();
|
const { selectedStoreId } = useStore();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const permissions = usePermissions(user?.role);
|
|
||||||
|
|
||||||
// Redirection pour les employés
|
// Redirection pour les employés
|
||||||
if (user?.role === 'employee') {
|
if (user?.role === 'employee') {
|
||||||
@@ -227,8 +224,7 @@ export default function Orders() {
|
|||||||
const canDelete = permissions.canDelete('orders');
|
const canDelete = permissions.canDelete('orders');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 min-h-0 flex flex-col">
|
<div className="p-6 space-y-6">
|
||||||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -289,7 +285,7 @@ export default function Orders() {
|
|||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
|
||||||
</div>
|
</div>
|
||||||
) : filteredOrders.length === 0 ? (
|
) : (console.log('📦 Filtered orders length:', filteredOrders.length) || filteredOrders.length === 0) ? (
|
||||||
<div className="flex flex-col items-center justify-center h-64 text-gray-500">
|
<div className="flex flex-col items-center justify-center h-64 text-gray-500">
|
||||||
<Package className="w-16 h-16 mb-4 text-gray-300" />
|
<Package className="w-16 h-16 mb-4 text-gray-300" />
|
||||||
<h3 className="text-lg font-medium mb-2">Aucune commande trouvée</h3>
|
<h3 className="text-lg font-medium mb-2">Aucune commande trouvée</h3>
|
||||||
@@ -309,10 +305,11 @@ export default function Orders() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white border border-gray-200 shadow-lg overflow-hidden rounded-xl">
|
<div className="flex-1 overflow-y-auto p-6">
|
||||||
<div className="overflow-x-auto">
|
<div className="bg-white border border-gray-200 shadow-lg overflow-hidden">
|
||||||
<table className="w-full">
|
<div className="overflow-x-auto">
|
||||||
<thead className="bg-gray-50 border-b border-gray-200">
|
<table className="w-full">
|
||||||
|
<thead className="bg-gray-50 border-b border-gray-200">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Fournisseur
|
Fournisseur
|
||||||
@@ -333,8 +330,8 @@ export default function Orders() {
|
|||||||
Actions
|
Actions
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{paginatedOrders.map((order) => (
|
{paginatedOrders.map((order) => (
|
||||||
<tr key={order.id} className="hover:bg-gray-50">
|
<tr key={order.id} className="hover:bg-gray-50">
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
@@ -407,20 +404,21 @@ export default function Orders() {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
totalPages={totalPages}
|
totalPages={totalPages}
|
||||||
totalItems={totalItems}
|
totalItems={totalItems}
|
||||||
itemsPerPage={itemsPerPage}
|
itemsPerPage={itemsPerPage}
|
||||||
onPageChange={setCurrentPage}
|
onPageChange={setCurrentPage}
|
||||||
onItemsPerPageChange={setItemsPerPage}
|
onItemsPerPageChange={setItemsPerPage}
|
||||||
className="mt-4 p-4 border-t border-gray-200"
|
className="mt-4 p-4 border-t border-gray-200"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -463,7 +461,6 @@ export default function Orders() {
|
|||||||
itemName={orderToDelete ? `${orderToDelete.supplier?.name} - ${safeFormat(orderToDelete.plannedDate, 'dd/MM/yyyy')}` : undefined}
|
itemName={orderToDelete ? `${orderToDelete.supplier?.name} - ${safeFormat(orderToDelete.plannedDate, 'dd/MM/yyyy')}` : undefined}
|
||||||
isLoading={deleteMutation.isPending}
|
isLoading={deleteMutation.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user