feat: add MobileApp wrapper with StoreProvider and integrate mobile-specific routing for mobile pages.

This commit is contained in:
Michael committed 2025-12-10 14:02:37 +01:00
1 parent 7f4c172eeb
commit 7c7a4478c3
3 files changed
+113 -104

No files matched your search

+36 -33
View File
@@ -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>
); );
} }
+71
View File
@@ -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>
);
}
+6 -71
View File
@@ -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>
);
}