developed ui for pos

This commit is contained in:
2026-06-26 10:37:20 +05:30
parent 93df333df1
commit c6a3ea51da
4 changed files with 1437 additions and 87 deletions

View File

@@ -1214,113 +1214,156 @@ export default function InventoryView({
</div> </div>
) : activeTab === 'requests' ? ( ) : activeTab === 'requests' ? (
<div className="flex-1 overflow-y-auto mt-4 pr-2 custom-scrollbar animate-in fade-in duration-300"> <div className="flex-1 overflow-y-auto mt-4 pr-2 custom-scrollbar animate-in fade-in duration-300">
<div className="flex justify-between items-center mb-6"> <div className="flex flex-col lg:flex-row lg:justify-between lg:items-end gap-4 mb-8">
<div> <div className="flex items-center gap-4">
<h2 className="text-xl font-black text-slate-900 tracking-tight flex items-center gap-2"> <div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-purple-50 to-indigo-50 border border-purple-100 flex items-center justify-center shadow-[0_2px_10px_-3px_rgba(102,37,130,0.15)] shrink-0">
<Inbox size={22} className="text-[#662582]" /> Pending Stock Requests <Inbox size={24} className="text-[#662582]" strokeWidth={2.5} />
</h2> </div>
<p className="text-xs font-bold text-slate-500 mt-1 uppercase tracking-widest">Store Branch Inventory Requests</p> <div>
<h2 className="text-2xl font-black text-slate-900 tracking-tight mb-1">
Pending Stock Requests
</h2>
<div className="text-sm font-semibold text-slate-500 flex items-center gap-2">
Store Branch Inventory Requests
<span className="px-2 py-0.5 rounded-md bg-slate-100 text-slate-600 text-[10px] font-bold tracking-wider uppercase border border-slate-200 shadow-sm">
{flattenedRequests.length} Total
</span>
</div>
</div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<select <div className="relative group">
value={requestStoreFilter} <select
onChange={(e) => setRequestStoreFilter(e.target.value)} value={requestStoreFilter}
className="bg-white border border-slate-200 text-slate-700 px-4 py-2 rounded-xl text-xs font-bold shadow-sm outline-none cursor-pointer focus:border-purple-300 transition-colors" onChange={(e) => setRequestStoreFilter(e.target.value)}
> className="appearance-none bg-white border border-slate-200 text-slate-700 pl-4 pr-10 py-2.5 rounded-xl text-sm font-semibold shadow-sm outline-none cursor-pointer hover:border-[#662582]/40 hover:shadow-md transition-all focus:border-[#662582] focus:ring-2 focus:ring-[#662582]/20"
<option value="All Stores">All Stores</option> >
{requestingStores.map(store => ( <option value="All Stores">All Stores</option>
<option key={store} value={store}>{store}</option> {requestingStores.map(store => (
))} <option key={store} value={store}>{store}</option>
</select> ))}
</select>
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-slate-400 group-hover:text-[#662582] transition-colors">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>
</div>
</div>
<button <button
onClick={() => setActiveTab('catalog')} onClick={() => setActiveTab('catalog')}
className="flex items-center gap-1.5 bg-white border border-slate-200 text-slate-600 hover:bg-slate-50 px-4 py-2 rounded-xl text-sm font-bold shadow-sm cursor-pointer transition-colors" className="flex items-center gap-2 bg-white border border-slate-200 text-slate-700 hover:text-slate-900 hover:bg-slate-50 px-4 py-2.5 rounded-xl text-sm font-bold shadow-sm hover:shadow-md cursor-pointer transition-all active:scale-95"
> >
← Back to Catalogue <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="m15 18-6-6 6-6"/></svg>
Back to Catalogue
</button> </button>
</div> </div>
</div> </div>
{flattenedRequests.length === 0 ? ( {flattenedRequests.length === 0 ? (
<div className="flex flex-col items-center justify-center h-64 bg-white/40 border border-slate-200 rounded-2xl"> <div className="flex flex-col items-center justify-center h-80 bg-gradient-to-b from-white to-slate-50 border border-slate-200/60 border-dashed rounded-2xl shadow-sm">
<Inbox size={48} className="text-slate-300 mb-4" /> <div className="w-20 h-20 rounded-full bg-slate-100/80 flex items-center justify-center mb-6 shadow-inner">
<h3 className="text-lg font-bold text-slate-500">No Pending Requests</h3> <Inbox size={32} className="text-slate-400" />
<p className="text-xs text-slate-400">All store stock requests have been processed.</p> </div>
<h3 className="text-xl font-bold text-slate-700 tracking-tight">No Pending Requests</h3>
<p className="text-sm font-medium text-slate-400 mt-2 text-center max-w-sm leading-relaxed">
You're all caught up! All store stock requests have been processed and there is currently no pending action required.
</p>
</div> </div>
) : ( ) : (
<div className="bg-white border rounded-2xl overflow-hidden" style={{ borderColor: BORDER }}> <div className="bg-white rounded-2xl border border-slate-200 shadow-[0_4px_20px_-4px_rgba(0,0,0,0.05)] overflow-hidden">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full" style={{ minWidth: 960 }}> <table className="w-full text-left border-collapse" style={{ minWidth: 960 }}>
<thead> <thead>
<tr> <tr className="bg-slate-50/80 border-b border-slate-200">
{['Requested At', 'Store', 'Product', 'Qty', 'Status', 'Resolved At', ''].map((h, i) => ( {['Requested At', 'Store', 'Product', 'Qty', 'Status', 'Resolved At', ''].map((h, i) => (
<th key={i} className="px-3 py-2.5 text-left" style={TH_STYLE}>{h}</th> <th key={i} className="px-5 py-4 text-[10px] font-black uppercase tracking-widest text-slate-500 whitespace-nowrap">{h}</th>
))} ))}
</tr> </tr>
</thead> </thead>
<tbody> <tbody className="divide-y divide-slate-100">
{flattenedRequests.map((req, idx) => { {flattenedRequests.map((req, idx) => {
const isApproved = req.pickData.status === 'Approved'; const isApproved = req.pickData.status === 'Approved';
const isRejected = req.pickData.status === 'Rejected'; const isRejected = req.pickData.status === 'Rejected';
const isPending = req.pickData.status === 'Pending'; const isPending = req.pickData.status === 'Pending';
const isCancelled = req.pickData.status === 'Cancelled'; const isCancelled = req.pickData.status === 'Cancelled';
const color = isApproved ? '#10b981' : isRejected ? '#f43f5e' : isCancelled ? '#94a3b8' : '#f59e0b';
const DIVIDER_C = '#f1f5f9';
return ( return (
<tr key={`${req.locationid}-${req.productid}-${idx}`} className="transition-colors" style={{ borderBottom: `1px solid ${DIVIDER_C}`, background: 'transparent' }} <tr
onMouseEnter={(e) => { e.currentTarget.style.background = SURFACE_ALT; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}> key={`${req.locationid}-${req.productid}-${idx}`}
<td className="px-3 py-2.5"> className="transition-all duration-200 hover:bg-slate-50/80 group cursor-pointer"
<span className="text-xs font-mono" style={{ color: TEXT_3 }}> onClick={() => setSelectedRequest(req)}
{req.pickData.requestedAt ? new Date(req.pickData.requestedAt).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : ''} >
</span> <td className="px-5 py-4 whitespace-nowrap">
<div className="flex flex-col">
<span className="text-sm font-semibold text-slate-900">
{req.pickData.requestedAt ? new Date(req.pickData.requestedAt).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' }) : '—'}
</span>
<span className="text-xs font-medium text-slate-500">
{req.pickData.requestedAt ? new Date(req.pickData.requestedAt).toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit' }) : ''}
</span>
</div>
</td> </td>
<td className="px-3 py-2.5"> <td className="px-5 py-4 whitespace-nowrap">
<span className="inline-flex items-center gap-1 font-bold text-[12px]" style={{ color: BRAND }}> <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-indigo-50/50 text-indigo-700 border border-indigo-100/50 transition-colors group-hover:bg-indigo-100/50">
<Store size={11} /> {req.locationname} <Store size={14} className="text-indigo-500" />
</span> <span className="font-bold text-xs">{req.locationname}</span>
</div>
</td> </td>
<td className="px-3 py-2.5"> <td className="px-5 py-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<img src={req.product?.image} alt={req.product?.name} className="w-8 h-8 rounded-md object-cover border" style={{ borderColor: BORDER }} /> <div className="relative w-10 h-10 rounded-xl overflow-hidden border border-slate-200 bg-white shadow-sm shrink-0 group-hover:shadow transition-shadow">
<div> <img src={req.product?.image} alt={req.product?.name} className="w-full h-full object-cover" />
<p className="font-bold text-[12px] truncate max-w-[200px]" style={{ color: TEXT }}>{req.product?.name}</p> </div>
<p className="text-[10px] truncate max-w-[200px]" style={{ color: TEXT_2 }}>{req.product?.sku}</p> <div className="min-w-0 flex-1">
<p className="font-bold text-sm text-slate-900 truncate group-hover:text-indigo-700 transition-colors">{req.product?.name}</p>
<p className="text-xs font-medium text-slate-500 truncate mt-0.5">{req.product?.sku}</p>
</div> </div>
</div> </div>
</td> </td>
<td className="px-3 py-2.5 font-mono text-[12px]" style={{ color: TEXT }}> <td className="px-5 py-4 whitespace-nowrap">
{req.pickData.qty || ''} <span className="inline-flex items-center justify-center min-w-[2rem] px-2 py-1 rounded-md bg-slate-100 text-slate-700 font-bold text-sm border border-slate-200/60">
{req.pickData.qty || '—'}
</span>
</td> </td>
<td className="px-3 py-2.5"> <td className="px-5 py-4 whitespace-nowrap">
<StatusChip label={req.pickData.status || ''} color={color} /> <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-bold border ${
isApproved ? 'bg-emerald-50 text-emerald-700 border-emerald-200' :
isRejected ? 'bg-rose-50 text-rose-700 border-rose-200' :
isCancelled ? 'bg-slate-50 text-slate-700 border-slate-200' :
'bg-amber-50 text-amber-700 border-amber-200'
}`}>
<span className={`w-1.5 h-1.5 rounded-full ${
isApproved ? 'bg-emerald-500' :
isRejected ? 'bg-rose-500' :
isCancelled ? 'bg-slate-400' :
'bg-amber-500 animate-pulse'
}`} />
{req.pickData.status || '—'}
</span>
</td> </td>
<td className="px-3 py-2.5"> <td className="px-5 py-4 whitespace-nowrap">
<span className="text-xs font-mono" style={{ color: TEXT_3 }}> <span className="text-xs font-medium text-slate-500">
{req.pickData.resolvedAt ? new Date(req.pickData.resolvedAt).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : '—'} {req.pickData.resolvedAt ? new Date(req.pickData.resolvedAt).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : '—'}
</span> </span>
</td> </td>
<td className="px-3 py-2.5 text-right"> <td className="px-5 py-4 whitespace-nowrap text-right">
{isPending ? ( {isPending ? (
<div className="flex items-center justify-end gap-2"> <div className="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
<button <button
onClick={(e) => { e.stopPropagation(); updateProductRequestStatus(req.locationid, req.productid, 'Approved'); }} onClick={(e) => { e.stopPropagation(); updateProductRequestStatus(req.locationid, req.productid, 'Approved'); }}
className="rounded-full font-extrabold cursor-pointer transition-colors text-white" className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold text-emerald-700 bg-emerald-50 hover:bg-emerald-100 hover:text-emerald-800 border border-emerald-200 transition-all active:scale-95"
style={{ padding: '4px 12px', fontSize: 11, background: '#10b981', border: '1px solid #059669' }} title="Approve Request"
title="Approve"
> >
Approve <CheckCircle size={14} /> Approve
</button> </button>
<button <button
onClick={(e) => { e.stopPropagation(); updateProductRequestStatus(req.locationid, req.productid, 'Rejected'); }} onClick={(e) => { e.stopPropagation(); updateProductRequestStatus(req.locationid, req.productid, 'Rejected'); }}
className="rounded-full font-extrabold cursor-pointer transition-colors" className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold text-rose-700 bg-rose-50 hover:bg-rose-100 hover:text-rose-800 border border-rose-200 transition-all active:scale-95"
style={{ padding: '4px 12px', fontSize: 11, color: '#f43f5e', background: tint('#f43f5e'), border: `1px solid ${edge('#f43f5e')}` }} title="Reject Request"
title="Reject"
> >
Reject <X size={14} /> Reject
</button> </button>
</div> </div>
) : ( ) : (
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest"> <span className="text-[10px] font-bold text-slate-300 uppercase tracking-widest">
Processed Processed
</span> </span>
)} )}

1280
src/components/PosView.tsx Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -540,40 +540,45 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
sub="Browse the catalogue and add items to your store to request stock." sub="Browse the catalogue and add items to your store to request stock."
/> />
) : ( ) : (
<div className="bg-white border rounded-2xl overflow-hidden" style={{ borderColor: BORDER }}> <div className="bg-white rounded-2xl border border-slate-200 shadow-[0_4px_20px_-4px_rgba(0,0,0,0.05)] overflow-hidden">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full" style={{ minWidth: 800 }}> <table className="w-full text-left border-collapse" style={{ minWidth: 800 }}>
<thead> <thead>
<tr> <tr className="bg-slate-50/80 border-b border-slate-200">
{['Requested At', 'Product', 'Qty', 'Status', 'Resolved At'].map((h, i) => ( {['Requested At', 'Product', 'Qty', 'Status', 'Resolved At'].map((h, i) => (
<th key={i} className="px-3 py-2.5 text-left" style={TH_STYLE}>{h}</th> <th key={i} className="px-5 py-4 text-[10px] font-black uppercase tracking-widest text-slate-500 whitespace-nowrap">{h}</th>
))} ))}
</tr> </tr>
</thead> </thead>
<tbody> <tbody className="divide-y divide-slate-100">
{Object.entries(picks).map(([pid, data]: [string, any]) => { {Object.entries(picks).map(([pid, data]: [string, any]) => {
const prod = products.find(p => p.id === pid); const prod = products.find(p => p.id === pid);
const isApproved = data.status === 'Approved'; const isApproved = data.status === 'Approved';
const isRejected = data.status === 'Rejected'; const isRejected = data.status === 'Rejected';
const color = isApproved ? '#10b981' : isRejected ? '#f43f5e' : data.status === 'Cancelled' ? '#94a3b8' : '#f59e0b'; const isCancelled = data.status === 'Cancelled';
const DIVIDER_C = '#f1f5f9';
return ( return (
<tr key={pid} className="transition-colors" style={{ borderBottom: `1px solid ${DIVIDER_C}`, background: 'transparent' }} <tr key={pid} className="transition-all duration-200 hover:bg-slate-50/80 group">
onMouseEnter={(e) => { e.currentTarget.style.background = SURFACE_ALT; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}> <td className="px-5 py-4 whitespace-nowrap">
<td className="px-3 py-2.5"> <div className="flex flex-col">
<span className="text-xs font-mono" style={{ color: TEXT_3 }}> <span className="text-sm font-semibold text-slate-900">
{data.requestedAt ? new Date(data.requestedAt).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : '—'} {data.requestedAt ? new Date(data.requestedAt).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' }) : '—'}
</span> </span>
<span className="text-xs font-medium text-slate-500">
{data.requestedAt ? new Date(data.requestedAt).toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit' }) : ''}
</span>
</div>
</td> </td>
<td className="px-3 py-2.5"> <td className="px-5 py-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{prod ? ( {prod ? (
<> <>
<img src={prod.image} alt={prod.name} className="w-8 h-8 rounded-md object-cover border" style={{ borderColor: BORDER }} /> <div className="relative w-10 h-10 rounded-xl overflow-hidden border border-slate-200 bg-white shadow-sm shrink-0 group-hover:shadow transition-shadow">
<div> <img src={prod.image} alt={prod.name} className="w-full h-full object-cover" />
<p className="font-bold text-[12px] truncate max-w-[200px]" style={{ color: TEXT }}>{prod.name}</p> </div>
<p className="text-[10px] truncate max-w-[200px]" style={{ color: TEXT_2 }}>{prod.sku}</p> <div className="min-w-0 flex-1">
<p className="font-bold text-sm text-slate-900 truncate group-hover:text-indigo-700 transition-colors">{prod.name}</p>
<p className="text-xs font-medium text-slate-500 truncate mt-0.5">{prod.sku}</p>
</div> </div>
</> </>
) : ( ) : (
@@ -581,14 +586,29 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
)} )}
</div> </div>
</td> </td>
<td className="px-3 py-2.5 font-mono text-[12px]" style={{ color: TEXT }}> <td className="px-5 py-4 whitespace-nowrap">
{data.qty || '—'} <span className="inline-flex items-center justify-center min-w-[2rem] px-2 py-1 rounded-md bg-slate-100 text-slate-700 font-bold text-sm border border-slate-200/60">
{data.qty || '—'}
</span>
</td> </td>
<td className="px-3 py-2.5"> <td className="px-5 py-4 whitespace-nowrap">
<StatusChip label={data.status || '—'} color={color} /> <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-bold border ${
isApproved ? 'bg-emerald-50 text-emerald-700 border-emerald-200' :
isRejected ? 'bg-rose-50 text-rose-700 border-rose-200' :
isCancelled ? 'bg-slate-50 text-slate-700 border-slate-200' :
'bg-amber-50 text-amber-700 border-amber-200'
}`}>
<span className={`w-1.5 h-1.5 rounded-full ${
isApproved ? 'bg-emerald-500' :
isRejected ? 'bg-rose-500' :
isCancelled ? 'bg-slate-400' :
'bg-amber-500 animate-pulse'
}`} />
{data.status || '—'}
</span>
</td> </td>
<td className="px-3 py-2.5"> <td className="px-5 py-4 whitespace-nowrap">
<span className="text-xs font-mono" style={{ color: TEXT_3 }}> <span className="text-xs font-medium text-slate-500">
{data.resolvedAt ? new Date(data.resolvedAt).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : '—'} {data.resolvedAt ? new Date(data.resolvedAt).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : '—'}
</span> </span>
</td> </td>

View File

@@ -34,6 +34,7 @@ import StoreCatalogView from './StoreCatalogView';
import DispatchHubView from './DispatchHubView'; import DispatchHubView from './DispatchHubView';
import DeliveryReportsView from './DeliveryReportsView'; import DeliveryReportsView from './DeliveryReportsView';
import StoreQRView from './StoreQRView'; import StoreQRView from './StoreQRView';
import PosView from './PosView';
import UserStoreSidebar, { type UserNavItem } from './UserStoreSidebar'; import UserStoreSidebar, { type UserNavItem } from './UserStoreSidebar';
import ComparisonModal from './ComparisonModal'; import ComparisonModal from './ComparisonModal';
interface UserStorePageProps { interface UserStorePageProps {
@@ -48,6 +49,7 @@ interface UserStorePageProps {
const NAV_ITEMS: UserNavItem[] = [ const NAV_ITEMS: UserNavItem[] = [
{ id: 'console', label: 'Store Console', icon: LayoutDashboard }, { id: 'console', label: 'Store Console', icon: LayoutDashboard },
{ id: 'inventory', label: 'Products', icon: Layers }, { id: 'inventory', label: 'Products', icon: Layers },
{ id: 'pos', label: 'POS Terminal', icon: ShoppingBag },
{ id: 'customers', label: 'Customers', icon: Users }, { id: 'customers', label: 'Customers', icon: Users },
{ id: 'dispatch', label: 'Dispatch', icon: Route }, { id: 'dispatch', label: 'Dispatch', icon: Route },
{ id: 'reports', label: 'Reports', icon: ClipboardList }, { id: 'reports', label: 'Reports', icon: ClipboardList },
@@ -191,6 +193,7 @@ export default function UserStorePage({ onLogout, user }: UserStorePageProps) {
// Logistics console — scoped to this user's store. These views own their // Logistics console — scoped to this user's store. These views own their
// loading/error states, so they don't need the store-console load gating below. // loading/error states, so they don't need the store-console load gating below.
if (activeSection === 'pos') return <PosView locationid={resolvedLocationId || undefined} tenantId={tenantId} />;
if (activeSection === 'dispatch') return <DispatchHubView locationid={resolvedLocationId || undefined} tenantId={tenantId} />; if (activeSection === 'dispatch') return <DispatchHubView locationid={resolvedLocationId || undefined} tenantId={tenantId} />;
if (activeSection === 'reports') return <DeliveryReportsView tenantId={tenantId} locationid={resolvedLocationId || undefined} />; if (activeSection === 'reports') return <DeliveryReportsView tenantId={tenantId} locationid={resolvedLocationId || undefined} />;
// Inventory & Catalog is its own page: the manager-curated catalog the user // Inventory & Catalog is its own page: the manager-curated catalog the user
@@ -278,6 +281,8 @@ export default function UserStorePage({ onLogout, user }: UserStorePageProps) {
? storeName ? storeName
: activeSection === 'inventory' : activeSection === 'inventory'
? 'Products' ? 'Products'
: activeSection === 'pos'
? 'POS Terminal'
: activeSection === 'account' : activeSection === 'account'
? 'My Account' ? 'My Account'
: activeSection.charAt(0).toUpperCase() + activeSection.slice(1), : activeSection.charAt(0).toUpperCase() + activeSection.slice(1),
@@ -285,6 +290,8 @@ export default function UserStorePage({ onLogout, user }: UserStorePageProps) {
? LayoutDashboard ? LayoutDashboard
: activeSection === 'inventory' : activeSection === 'inventory'
? Layers ? Layers
: activeSection === 'pos'
? ShoppingBag
: activeSection === 'customers' : activeSection === 'customers'
? Users ? Users
: activeSection === 'dispatch' : activeSection === 'dispatch'
@@ -307,10 +314,10 @@ export default function UserStorePage({ onLogout, user }: UserStorePageProps) {
className={`flex-1 min-w-0 transition-all duration-300 ${ className={`flex-1 min-w-0 transition-all duration-300 ${
// Dispatch is a full-bleed cockpit — fill the area exactly (no page // Dispatch is a full-bleed cockpit — fill the area exactly (no page
// padding) so it sits flush under the header. Other pages stay padded. // padding) so it sits flush under the header. Other pages stay padded.
activeSection === 'dispatch' ? 'h-[calc(100vh-64px)] overflow-hidden' : 'min-h-[calc(100vh-64px)]' activeSection === 'dispatch' || activeSection === 'pos' ? 'h-[calc(100vh-64px)] overflow-hidden' : 'min-h-[calc(100vh-64px)]'
} ${sidebarOpen ? 'md:pl-64' : 'md:pl-20'}`} } ${sidebarOpen ? 'md:pl-64' : 'md:pl-20'}`}
> >
{activeSection === 'dispatch' ? ( {activeSection === 'dispatch' || activeSection === 'pos' ? (
<div className="w-full h-full">{renderSection()}</div> <div className="w-full h-full">{renderSection()}</div>
) : ( ) : (
<div className="w-full p-container-margin md:p-xl space-y-lg transition-all duration-300"> <div className="w-full p-container-margin md:p-xl space-y-lg transition-all duration-300">