Restored to '479577c4ff1b136dd2b1cea9dffd6c0fc0018d59'

Replit-Restored-To: 479577c4ff
This commit is contained in:
michaelschal committed 2025-08-12 20:42:40 +00:00
1 parent 1346c9b34c
commit a4a637d415
13 files changed
+128 -347

No files matched your search

+4 -4
View File
@@ -99,7 +99,7 @@ export function CustomerOrderForm({
quantity: order?.quantity || 1,
supplierId: order?.supplierId || undefined,
status: "En attente de Commande", // Statut fixe
deposit: Number(order?.deposit) || 0,
deposit: order?.deposit || 0,
isPromotionalPrice: order?.isPromotionalPrice || false,
customerNotified: order?.customerNotified || false,
groupId: getDefaultGroupId(), // ✅ PRIORITÉ CORRECTE APPLIQUÉE
@@ -125,7 +125,7 @@ export function CustomerOrderForm({
selectedStoreId,
userHasGroups: !!user?.userGroups,
userGroupsLength: user?.userGroups?.length,
userGroups: user?.userGroups?.map((ug: any) => ({
userGroups: user?.userGroups?.map(ug => ({
groupId: ug.groupId || ug.group?.id,
groupName: ug.group?.name,
fullGroupData: ug
@@ -156,7 +156,7 @@ export function CustomerOrderForm({
contactNumber: data.contactNumber || '',
productName: data.productName || '',
quantity: data.quantity,
groupId: typeof groupId === 'number' ? groupId : parseInt(groupId?.toString() || '1'),
groupId: typeof groupId === 'number' ? groupId : parseInt(groupId.toString()),
isPickup: false,
notes: data.notes,
deposit: data.deposit || 0,
@@ -173,7 +173,7 @@ export function CustomerOrderForm({
// Get available groups for the user
const availableGroups = user?.role === 'admin'
? groups
: user?.userGroups?.map((ug: any) => ug.group) || [];
: user?.userGroups?.map(ug => ug.group) || [];
// Auto-select group for users respecting admin store selection
const currentGroupId = form.getValues('groupId');
+11 -64
View File
@@ -2,9 +2,8 @@ import { ReactNode, useState, createContext, useContext } 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, X } from "lucide-react";
import { LogOut, Store } from "lucide-react";
import { useAuthUnified } from "@/hooks/useAuthUnified";
import { useResponsive } from "@/hooks/useResponsive";
import Sidebar from "./Sidebar";
import type { Group } from "@shared/schema";
@@ -14,8 +13,6 @@ interface StoreContextType {
stores: Group[];
sidebarCollapsed: boolean;
setSidebarCollapsed: (collapsed: boolean) => void;
mobileMenuOpen: boolean;
setMobileMenuOpen: (open: boolean) => void;
}
const StoreContext = createContext<StoreContextType | undefined>(undefined);
@@ -35,8 +32,6 @@ interface LayoutProps {
export default function Layout({ children }: LayoutProps) {
const { user } = useAuthUnified();
const queryClient = useQueryClient();
const { isMobile, isTablet, isDesktop } = useResponsive();
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
// Restaurer le selectedStoreId depuis localStorage si disponible
const saved = localStorage.getItem('selectedStoreId');
@@ -48,12 +43,9 @@ export default function Layout({ children }: LayoutProps) {
const [sidebarCollapsed, setSidebarCollapsed] = useState<boolean>(() => {
// Restaurer l'état de la sidebar depuis localStorage
const saved = localStorage.getItem('sidebarCollapsed');
// Sur mobile, toujours collapsé par défaut
return saved ? JSON.parse(saved) : isMobile;
return saved ? JSON.parse(saved) : false;
});
const [mobileMenuOpen, setMobileMenuOpen] = useState<boolean>(false);
const { data: stores = [] } = useQuery<Group[]>({
queryKey: ['/api/groups'],
enabled: !!user,
@@ -64,56 +56,20 @@ export default function Layout({ children }: LayoutProps) {
};
return (
<StoreContext.Provider value={{
selectedStoreId,
setSelectedStoreId,
stores,
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'
}`}>
<StoreContext.Provider value={{ selectedStoreId, setSelectedStoreId, stores, sidebarCollapsed, setSidebarCollapsed }}>
<div className="flex h-screen overflow-hidden bg-gray-50">
<Sidebar />
<main className="flex-1 flex flex-col overflow-hidden">
{/* 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">
{/* Mobile menu button */}
{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>
<h1 className="text-lg font-medium text-gray-800">LogiFlow</h1>
</div>
{/* Store selector for admin - moved to top right */}
{user?.role === 'admin' && stores.length > 0 && (
<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
value={selectedStoreId?.toString() || "all"}
onValueChange={(value) => {
@@ -155,7 +111,7 @@ export default function Layout({ children }: LayoutProps) {
<div className="flex items-center gap-2">
<div
className="w-3 h-3"
style={{ backgroundColor: store.color || '#6B7280' }}
style={{ backgroundColor: store.color }}
/>
<span>{store.name}</span>
</div>
@@ -167,19 +123,10 @@ export default function Layout({ children }: LayoutProps) {
)}
</header>
{/* Main content area */}
<div className="flex-1 bg-gray-50 dark:bg-gray-900 overflow-hidden">
<div className="flex-1 overflow-auto bg-gray-50 p-6">
{children}
</div>
</main>
{/* Mobile menu close overlay */}
{isMobile && mobileMenuOpen && (
<div
className="fixed inset-0 bg-black/20 z-30"
onClick={() => setMobileMenuOpen(false)}
/>
)}
</div>
</StoreContext.Provider>
);
+59 -77
View File
@@ -1,6 +1,5 @@
import { Link, useLocation } from "wouter";
import { useAuthSimple } from "@/hooks/useAuthSimple";
import { useResponsive } from "@/hooks/useResponsive";
import { Button } from "@/components/ui/button";
import { useStore } from "./Layout";
import {
@@ -26,8 +25,7 @@ import {
export default function Sidebar() {
const { user, isLoading, error } = useAuthSimple();
const [location] = useLocation();
const { isMobile, isTablet, isDesktop } = useResponsive();
const { sidebarCollapsed, setSidebarCollapsed, mobileMenuOpen, setMobileMenuOpen } = useStore();
const { sidebarCollapsed, setSidebarCollapsed } = useStore();
// Debug logging pour production
console.log('Sidebar - User:', user);
@@ -50,20 +48,9 @@ export default function Sidebar() {
};
const toggleSidebar = () => {
if (isMobile) {
setMobileMenuOpen(!mobileMenuOpen);
} else {
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 newCollapsed = !sidebarCollapsed;
setSidebarCollapsed(newCollapsed);
localStorage.setItem('sidebarCollapsed', JSON.stringify(newCollapsed));
};
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
if (isLoading) {
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">
{!sidebarCollapsed && (
<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
if (!user) {
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">
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
@@ -259,38 +243,36 @@ export default function Sidebar() {
}
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 */}
<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">
{!isCollapsed && (
<div className="h-16 flex items-center justify-between border-b border-gray-200 bg-white px-3">
{!sidebarCollapsed && (
<div className="flex items-center space-x-3">
<Store className="h-6 w-6 text-blue-600 dark:text-blue-400" />
<span className="text-lg font-bold text-gray-900 dark:text-gray-100">LogiFlow</span>
<Store className="h-6 w-6 text-blue-600" />
<span className="text-lg font-semibold text-gray-900">LogiFlow</span>
</div>
)}
{isCollapsed && (
{sidebarCollapsed && (
<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>
)}
{!isMobile && (
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100 dark:hover:bg-gray-700"
>
{isCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSidebar}
className="h-8 w-8 p-0 hover:bg-gray-100"
>
{sidebarCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</div>
{/* 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">
{menuItems.map((item) => {
const hasRolePermission = hasPermission(item.roles);
@@ -308,16 +290,16 @@ export default function Sidebar() {
return (
<Link key={item.path} href={item.path}>
<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
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-r-2 border-blue-600 dark:border-blue-400'
: 'text-gray-700 dark:text-gray-300'
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700'
: 'text-gray-700'
}`}
title={isCollapsed ? item.label : undefined}
onClick={handleLinkClick}
title={sidebarCollapsed ? item.label : undefined}
onClick={() => console.log(`Navigating to: ${item.path}`)}
>
<Icon className={`${isCollapsed ? 'h-5 w-5' : 'h-4 w-4 mr-3'}`} />
{!isCollapsed && item.label}
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
{!sidebarCollapsed && item.label}
</div>
</Link>
);
@@ -327,14 +309,14 @@ export default function Sidebar() {
{/* Management Section */}
{managementItems.some(item => hasPermission(item.roles)) && (
<>
{!isCollapsed && (
{!sidebarCollapsed && (
<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
</h3>
</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">
{managementItems.map((item) => {
if (!hasPermission(item.roles)) return null;
@@ -345,16 +327,16 @@ export default function Sidebar() {
return (
<Link key={item.path} href={item.path}>
<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
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-r-2 border-blue-600 dark:border-blue-400'
: 'text-gray-700 dark:text-gray-300'
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700'
: 'text-gray-700'
}`}
title={isCollapsed ? item.label : undefined}
onClick={handleLinkClick}
title={sidebarCollapsed ? item.label : undefined}
onClick={() => console.log(`Navigating to management: ${item.path}`)}
>
<Icon className={`${isCollapsed ? 'h-5 w-5' : 'h-4 w-4 mr-3'}`} />
{!isCollapsed && item.label}
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
{!sidebarCollapsed && item.label}
</div>
</Link>
);
@@ -366,10 +348,10 @@ export default function Sidebar() {
{/* Administration Section */}
{adminItems.some(item => hasPermission(item.roles)) && (
<div className="border-t border-gray-200 dark:border-gray-700 py-4 px-3">
{!isCollapsed && (
<div className="border-t border-gray-200 py-4 px-3">
{!sidebarCollapsed && (
<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
</h3>
</div>
@@ -384,16 +366,16 @@ export default function Sidebar() {
return (
<Link key={item.path} href={item.path}>
<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
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-r-2 border-blue-600 dark:border-blue-400'
: 'text-gray-700 dark:text-gray-300'
? 'bg-gray-100 text-gray-900 border-r-2 border-gray-700'
: 'text-gray-700'
}`}
title={isCollapsed ? item.label : undefined}
onClick={handleLinkClick}
title={sidebarCollapsed ? item.label : undefined}
onClick={() => console.log(`Navigating to admin: ${item.path}`)}
>
<Icon className={`${isCollapsed ? 'h-5 w-5' : 'h-4 w-4 mr-3'}`} />
{!isCollapsed && item.label}
<Icon className={`h-4 w-4 ${sidebarCollapsed ? '' : 'mr-3'}`} />
{!sidebarCollapsed && item.label}
</div>
</Link>
);
@@ -403,20 +385,20 @@ export default function Sidebar() {
)}
{/* User Profile & Logout */}
<div className="border-t border-gray-200 dark:border-gray-700 p-4">
{!isCollapsed ? (
<div className="border-t border-gray-200 p-4">
{!sidebarCollapsed ? (
<>
<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">
<span className="text-xs font-medium text-gray-700 dark:text-gray-300">
<div className="h-8 w-8 bg-gray-100 flex items-center justify-center">
<span className="text-xs font-medium text-gray-700">
{getInitials(user?.firstName, user?.lastName)}
</span>
</div>
<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}
</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 === 'manager' ? 'Manager' :
user?.role === 'directeur' ? 'Directeur' : 'Employé'}