mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-24 15:38:18 +02:00
feat: add MobileApp wrapper with StoreProvider and integrate mobile-specific routing for mobile pages.
This commit is contained in:
1 parent
7f4c172eeb
commit
7c7a4478c3
3 files changed
+113
-104
No files matched your search
@@ -30,6 +30,7 @@ import PaymentSchedulePage from "@/pages/PaymentSchedulePage";
|
|||||||
import Layout from "@/components/Layout";
|
import Layout from "@/components/Layout";
|
||||||
|
|
||||||
// Mobile pages
|
// Mobile pages
|
||||||
|
import MobileApp from "@/pages/mobile/MobileApp";
|
||||||
import MobileDashboardPage from "@/pages/mobile/DashboardPage";
|
import MobileDashboardPage from "@/pages/mobile/DashboardPage";
|
||||||
import MobileOrdersPage from "@/pages/mobile/OrdersPage";
|
import MobileOrdersPage from "@/pages/mobile/OrdersPage";
|
||||||
import MobileDeliveriesPage from "@/pages/mobile/DeliveriesPage";
|
import MobileDeliveriesPage from "@/pages/mobile/DeliveriesPage";
|
||||||
@@ -85,44 +86,46 @@ function RouterProduction() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== MOBILE ROUTES (sans Layout standard) =====
|
// ===== MOBILE ROUTES (avec MobileApp pour StoreProvider) =====
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<MobileApp>
|
||||||
<Route path="/calendar" component={MobileCalendarPage} />
|
<Switch>
|
||||||
<Route path="/dashboard" component={MobileDashboardPage} />
|
<Route path="/calendar" component={MobileCalendarPage} />
|
||||||
<Route path="/orders" component={MobileOrdersPage} />
|
<Route path="/dashboard" component={MobileDashboardPage} />
|
||||||
<Route path="/deliveries" component={MobileDeliveriesPage} />
|
<Route path="/orders" component={MobileOrdersPage} />
|
||||||
<Route path="/tasks" component={MobileTasksPage} />
|
<Route path="/deliveries" component={MobileDeliveriesPage} />
|
||||||
|
<Route path="/tasks" component={MobileTasksPage} />
|
||||||
|
|
||||||
{/* Pages sans version mobile - utiliser version desktop dans Layout pour l'instant */}
|
{/* Pages sans version mobile - utiliser version desktop pour l'instant */}
|
||||||
<Route path="/suppliers" component={Suppliers} />
|
<Route path="/suppliers" component={Suppliers} />
|
||||||
<Route path="/groups" component={Groups} />
|
<Route path="/groups" component={Groups} />
|
||||||
<Route path="/users" component={Users} />
|
<Route path="/users" component={Users} />
|
||||||
<Route path="/bl-reconciliation" component={BLReconciliation} />
|
<Route path="/bl-reconciliation" component={BLReconciliation} />
|
||||||
<Route path="/publicities" component={Publicities} />
|
<Route path="/publicities" component={Publicities} />
|
||||||
<Route path="/customer-orders" component={CustomerOrders} />
|
<Route path="/customer-orders" component={CustomerOrders} />
|
||||||
<Route path="/dlc" component={DlcPage} />
|
<Route path="/dlc" component={DlcPage} />
|
||||||
<Route path="/utilities" component={Utilities} />
|
<Route path="/utilities" component={Utilities} />
|
||||||
<Route path="/sav" component={SavTickets} />
|
<Route path="/sav" component={SavTickets} />
|
||||||
<Route path="/avoirs" component={Avoirs} />
|
<Route path="/avoirs" component={Avoirs} />
|
||||||
<Route path="/analytics" component={Analytics} />
|
<Route path="/analytics" component={Analytics} />
|
||||||
<Route path="/sales-analysis" component={SalesAnalysisPage} />
|
<Route path="/sales-analysis" component={SalesAnalysisPage} />
|
||||||
<Route path="/payment-schedule" component={PaymentSchedulePage} />
|
<Route path="/payment-schedule" component={PaymentSchedulePage} />
|
||||||
|
|
||||||
{/* Redirection /auth vers dashboard */}
|
{/* Redirection /auth vers dashboard */}
|
||||||
<Route path="/auth">
|
<Route path="/auth">
|
||||||
{() => {
|
{() => {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.location.href = '/dashboard';
|
window.location.href = '/dashboard';
|
||||||
}
|
}
|
||||||
return <MobileDashboardPage />;
|
return <MobileDashboardPage />;
|
||||||
}}
|
}}
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path="/" component={MobileDashboardPage} />
|
<Route path="/" component={MobileDashboardPage} />
|
||||||
<Route component={NotFound} />
|
<Route component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
</MobileApp>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* MobileApp - Wrapper pour l'application mobile avec StoreProvider
|
||||||
|
* Fournit le contexte Store à toutes les pages mobiles
|
||||||
|
*/
|
||||||
|
import { ReactNode, useState, useEffect } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||||
|
import { StoreProvider } from "@/contexts/StoreContext";
|
||||||
|
import type { Group } from "@shared/schema";
|
||||||
|
|
||||||
|
interface MobileAppProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MobileApp({ children }: MobileAppProps) {
|
||||||
|
const { user } = useAuthUnified();
|
||||||
|
|
||||||
|
// Store state management
|
||||||
|
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
|
||||||
|
if (typeof window === 'undefined') return null;
|
||||||
|
const saved = localStorage.getItem('selectedStoreId');
|
||||||
|
return saved ? parseInt(saved) : null;
|
||||||
|
});
|
||||||
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
|
const [storeInitialized, setStoreInitialized] = useState(false);
|
||||||
|
|
||||||
|
// Fetch stores
|
||||||
|
const { data: stores = [] } = useQuery<Group[]>({
|
||||||
|
queryKey: ['/api/groups'],
|
||||||
|
enabled: !!user,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize store selection
|
||||||
|
useEffect(() => {
|
||||||
|
if (user && stores.length > 0) {
|
||||||
|
if ((user.role === 'directeur' || user.role === 'manager') && !selectedStoreId && stores.length === 1) {
|
||||||
|
const singleStoreId = stores[0].id;
|
||||||
|
setSelectedStoreId(singleStoreId);
|
||||||
|
localStorage.setItem('selectedStoreId', singleStoreId.toString());
|
||||||
|
}
|
||||||
|
setStoreInitialized(true);
|
||||||
|
}
|
||||||
|
}, [user, stores, selectedStoreId]);
|
||||||
|
|
||||||
|
// Sync selectedStoreId to localStorage
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedStoreId) {
|
||||||
|
localStorage.setItem('selectedStoreId', selectedStoreId.toString());
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('selectedStoreId');
|
||||||
|
}
|
||||||
|
}, [selectedStoreId]);
|
||||||
|
|
||||||
|
const storeContextValue = {
|
||||||
|
selectedStoreId,
|
||||||
|
setSelectedStoreId,
|
||||||
|
stores,
|
||||||
|
sidebarCollapsed,
|
||||||
|
setSidebarCollapsed,
|
||||||
|
mobileMenuOpen,
|
||||||
|
setMobileMenuOpen,
|
||||||
|
storeInitialized
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StoreProvider value={storeContextValue}>
|
||||||
|
{children}
|
||||||
|
</StoreProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* MobileLayout - Layout dédié pour l'application mobile
|
* MobileLayout - Layout dédié pour l'application mobile
|
||||||
* Affiche le magasin sélectionné de manière visible et une navigation bottom
|
* Affiche le magasin sélectionné de manière visible et une navigation bottom
|
||||||
* Inclut son propre StoreProvider
|
* Note: Le StoreProvider est fourni par MobileApp au niveau supérieur
|
||||||
*/
|
*/
|
||||||
import { ReactNode, useState, useEffect } from "react";
|
import { ReactNode, useState } from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
|
||||||
import { Menu, Store, ChevronDown, LogOut } from "lucide-react";
|
import { Menu, Store, ChevronDown, LogOut } from "lucide-react";
|
||||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||||
import { StoreProvider } from "@/contexts/StoreContext";
|
import { useStore } from "@/contexts/StoreContext";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
@@ -19,15 +18,9 @@ interface MobileLayoutProps {
|
|||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inner component that uses the store context
|
export default function MobileLayout({ children, title }: MobileLayoutProps) {
|
||||||
function MobileLayoutInner({ children, title, selectedStoreId, setSelectedStoreId, stores, user }: {
|
const { user } = useAuthUnified();
|
||||||
children: ReactNode;
|
const { selectedStoreId, setSelectedStoreId, stores } = useStore();
|
||||||
title?: string;
|
|
||||||
selectedStoreId: number | null;
|
|
||||||
setSelectedStoreId: (id: number | null) => void;
|
|
||||||
stores: Group[];
|
|
||||||
user: any;
|
|
||||||
}) {
|
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
|
||||||
// Get selected store name
|
// Get selected store name
|
||||||
@@ -183,61 +176,3 @@ function MobileLayoutInner({ children, title, selectedStoreId, setSelectedStoreI
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main MobileLayout component with StoreProvider
|
|
||||||
export default function MobileLayout({ children, title }: MobileLayoutProps) {
|
|
||||||
const { user } = useAuthUnified();
|
|
||||||
|
|
||||||
// Store state management
|
|
||||||
const [selectedStoreId, setSelectedStoreId] = useState<number | null>(() => {
|
|
||||||
const saved = localStorage.getItem('selectedStoreId');
|
|
||||||
return saved ? parseInt(saved) : null;
|
|
||||||
});
|
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
||||||
const [storeInitialized, setStoreInitialized] = useState(false);
|
|
||||||
|
|
||||||
// Fetch stores
|
|
||||||
const { data: stores = [] } = useQuery<Group[]>({
|
|
||||||
queryKey: ['/api/groups'],
|
|
||||||
enabled: !!user,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize store selection
|
|
||||||
useEffect(() => {
|
|
||||||
if (user && stores.length > 0) {
|
|
||||||
if ((user.role === 'directeur' || user.role === 'manager') && !selectedStoreId && stores.length === 1) {
|
|
||||||
const singleStoreId = stores[0].id;
|
|
||||||
setSelectedStoreId(singleStoreId);
|
|
||||||
localStorage.setItem('selectedStoreId', singleStoreId.toString());
|
|
||||||
}
|
|
||||||
setStoreInitialized(true);
|
|
||||||
}
|
|
||||||
}, [user, stores, selectedStoreId]);
|
|
||||||
|
|
||||||
const storeContextValue = {
|
|
||||||
selectedStoreId,
|
|
||||||
setSelectedStoreId,
|
|
||||||
stores,
|
|
||||||
sidebarCollapsed,
|
|
||||||
setSidebarCollapsed,
|
|
||||||
mobileMenuOpen,
|
|
||||||
setMobileMenuOpen,
|
|
||||||
storeInitialized
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StoreProvider value={storeContextValue}>
|
|
||||||
<MobileLayoutInner
|
|
||||||
title={title}
|
|
||||||
selectedStoreId={selectedStoreId}
|
|
||||||
setSelectedStoreId={setSelectedStoreId}
|
|
||||||
stores={stores}
|
|
||||||
user={user}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</MobileLayoutInner>
|
|
||||||
</StoreProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in new issue
Block a user