reponsived the website and login fix done

This commit is contained in:
2026-07-21 16:26:45 +05:30
parent 407574d62a
commit f62668e54e
16 changed files with 11203 additions and 11193 deletions

View File

@@ -115,7 +115,18 @@ export default function App() {
const [isCoimbatoreView, setIsCoimbatoreView] = useState(false); const [isCoimbatoreView, setIsCoimbatoreView] = useState(false);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [sidebarOpen, setSidebarOpen] = useState(true); const [sidebarOpen, setSidebarOpen] = useState(() => typeof window !== 'undefined' ? window.innerWidth >= 768 : true);
// Auto adapt sidebar state on window resize
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 768) {
setSidebarOpen(false);
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
// Scope every Fiesta query to the signed-in merchant. The login record carries // Scope every Fiesta query to the signed-in merchant. The login record carries
// the user's tenantid; fall back to the shared constant only when it's absent // the user's tenantid; fall back to the shared constant only when it's absent
@@ -627,8 +638,8 @@ export default function App() {
/> />
{/* Main core pages payload area */} {/* Main core pages payload area */}
<main className={`flex-1 min-w-0 transition-all duration-300 ${sidebarOpen ? 'md:pl-64' : 'md:pl-16'} ${currentSection === 'inventory' || currentSection === 'dispatch' ? 'h-[calc(100vh-64px)] overflow-hidden' : 'min-h-[calc(100vh-64px)]'}`}> <main className={`flex-1 min-w-0 transition-all duration-300 ${sidebarOpen ? 'md:pl-64' : 'md:pl-16'} ${currentSection === 'inventory' || currentSection === 'dispatch' ? 'md:h-[calc(100vh-64px)] md:overflow-hidden min-h-[calc(100vh-64px)]' : 'min-h-[calc(100vh-64px)]'}`}>
<div className={`w-full transition-all duration-300 ${currentSection === 'dispatch' ? 'h-full overflow-hidden' : currentSection === 'inventory' ? 'h-full px-4 md:px-6 pt-2 pb-4 overflow-hidden' : currentSection === 'settings' ? 'p-4 md:p-6 space-y-lg' : 'p-container-margin md:p-xl space-y-lg'}`}> <div className={`w-full transition-all duration-300 ${currentSection === 'dispatch' ? 'h-full md:overflow-hidden p-2 sm:p-4' : currentSection === 'inventory' ? 'h-full px-2 sm:px-4 md:px-6 pt-2 pb-4 md:overflow-hidden' : currentSection === 'settings' ? 'p-3 sm:p-4 md:p-6 space-y-md sm:space-y-lg' : 'p-3 sm:p-5 md:p-xl space-y-md sm:space-y-lg'}`}>
<Routes> <Routes>
<Route index element={<Navigate to="dashboard" replace />} /> <Route index element={<Navigate to="dashboard" replace />} />
<Route path="dashboard" element={ <Route path="dashboard" element={

View File

@@ -155,7 +155,7 @@ export default function DashboardView({ searchQuery, tenantId = FIESTA_TENANT_ID
Live Core Live Core
</span> </span>
</h1> </h1>
<p className="text-slate-300 font-sans text-sm mt-2 leading-relaxed whitespace-nowrap"> <p className="text-slate-300 font-sans text-sm mt-2 leading-relaxed">
Month-to-date order operations for <strong className="text-white font-semibold">{tenantName}</strong>, pulled live from the API. Month-to-date order operations for <strong className="text-white font-semibold">{tenantName}</strong>, pulled live from the API.
</p> </p>
@@ -202,7 +202,7 @@ export default function DashboardView({ searchQuery, tenantId = FIESTA_TENANT_ID
)} )}
{/* KPI cards — all live from getordersummary / getinvoiceinsight */} {/* KPI cards — all live from getordersummary / getinvoiceinsight */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-gutter"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-gutter">
{kpis.map((kpi) => { {kpis.map((kpi) => {
const Icon = kpi.icon; const Icon = kpi.icon;
return ( return (

View File

@@ -146,7 +146,7 @@ export default function DeliveriesView({ searchQuery = '', locationid, tenantId
{/* Status tabs + search */} {/* Status tabs + search */}
<FilterBar className="mb-4"> <FilterBar className="mb-4">
<div className="flex flex-col lg:flex-row lg:items-center gap-3"> <div className="flex flex-col lg:flex-row lg:items-center gap-3">
<div className="flex items-center gap-2 overflow-x-auto py-0.5 flex-1 min-w-0"> <div className="flex items-center gap-2 overflow-x-auto custom-scrollbar no-scrollbar py-0.5 flex-1 min-w-0 touch-scrolling">
{STATUS_TABS.map((t) => ( {STATUS_TABS.map((t) => (
<React.Fragment key={t.key}> <React.Fragment key={t.key}>
<Pill active={status === t.key} color={BRAND} onClick={() => setStatus(t.key)} count={statusCounts[t.key] ?? 0}>{t.label}</Pill> <Pill active={status === t.key} color={BRAND} onClick={() => setStatus(t.key)} count={statusCounts[t.key] ?? 0}>{t.label}</Pill>

View File

@@ -45,24 +45,24 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
}; };
return ( return (
<div className="flex flex-col h-[calc(100vh-80px)] w-full"> <div className="flex flex-col min-h-[calc(100vh-80px)] md:h-[calc(100vh-80px)] w-full">
<div className="flex flex-col flex-none bg-white"> <div className="flex flex-col flex-none bg-white">
{/* ── Unified Hub Header ── */} {/* ── Unified Hub Header ── */}
<div className="flex items-center justify-between px-6 h-14 border-b border-slate-200"> <div className="flex flex-col sm:flex-row sm:items-center justify-between px-3 sm:px-6 py-2 sm:py-0 sm:h-14 gap-2 border-b border-slate-200">
<div className="flex items-center gap-5"> <div className="flex items-center gap-3 sm:gap-5">
<div className="flex items-center gap-3"> <div className="flex items-center gap-2 sm:gap-3">
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center font-extrabold text-sm text-white shadow-sm">C</div> <div className="w-7 h-7 sm:w-8 sm:h-8 rounded-lg bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center font-extrabold text-xs sm:text-sm text-white shadow-sm">C</div>
<div className="text-lg font-extrabold text-slate-800 tracking-tight">Console</div> <div className="text-base sm:text-lg font-extrabold text-slate-800 tracking-tight">Console</div>
<div className="relative inline-block ml-1"> <div className="relative inline-block ml-1">
<span className="flex items-center gap-1 px-2.5 py-1 bg-purple-50 text-purple-700 border border-purple-200/60 rounded-full text-[11px] font-bold cursor-default shadow-sm"> <span className="flex items-center gap-1 px-2 sm:px-2.5 py-0.5 sm:py-1 bg-purple-50 text-purple-700 border border-purple-200/60 rounded-full text-[10px] sm:text-[11px] font-bold cursor-default shadow-sm">
<MapPin size={12} className="text-purple-500" /> <MapPin size={12} className="text-purple-500" />
<span className="max-w-[180px] truncate">Coimbatore</span> <span className="max-w-[120px] sm:max-w-[180px] truncate">Coimbatore</span>
</span> </span>
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center justify-between sm:justify-end gap-2 sm:gap-4">
{deliveriesQ.isLoading ? ( {deliveriesQ.isLoading ? (
<span className="flex items-center gap-2 text-xs font-semibold text-slate-500"> <span className="flex items-center gap-2 text-xs font-semibold text-slate-500">
<span className="w-2 h-2 rounded-full bg-slate-300 animate-pulse" /> Syncing <span className="w-2 h-2 rounded-full bg-slate-300 animate-pulse" /> Syncing
@@ -72,26 +72,26 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
<span className="w-2 h-2 rounded-full bg-rose-500" /> Offline <span className="w-2 h-2 rounded-full bg-rose-500" /> Offline
</span> </span>
) : totalOrders === 0 ? ( ) : totalOrders === 0 ? (
<span className="flex items-center gap-2 text-xs font-semibold text-slate-500 bg-slate-100 px-3 py-1.5 rounded-full" title="No deliveries dispatched for this day"> <span className="flex items-center gap-1.5 sm:gap-2 text-[10px] sm:text-xs font-semibold text-slate-500 bg-slate-100 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full" title="No deliveries dispatched for this day">
<span className="w-2 h-2 rounded-full bg-slate-400" /> No deliveries today <span className="w-2 h-2 rounded-full bg-slate-400" /> No deliveries today
</span> </span>
) : ( ) : (
<span className="flex items-center gap-2 text-xs font-semibold text-emerald-600 bg-emerald-50 px-3 py-1.5 rounded-full border border-emerald-100"> <span className="flex items-center gap-1.5 sm:gap-2 text-[10px] sm:text-xs font-semibold text-emerald-600 bg-emerald-50 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full border border-emerald-100">
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" /> Live · {totalOrders} orders <span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" /> Live · {totalOrders} orders
</span> </span>
)} )}
<div className={`flex items-center bg-slate-50 border border-slate-200 rounded-xl p-1 shadow-sm ${isToday ? 'ring-1 ring-blue-500/20' : ''}`}> <div className={`flex items-center bg-slate-50 border border-slate-200 rounded-xl p-0.5 sm:p-1 shadow-sm ${isToday ? 'ring-1 ring-blue-500/20' : ''}`}>
<button className="p-1.5 rounded-lg text-slate-400 hover:text-slate-700 hover:bg-slate-200/50 transition-colors" onClick={() => shiftDate(-1)} title="Previous day"> <button className="p-1 sm:p-1.5 rounded-lg text-slate-400 hover:text-slate-700 hover:bg-slate-200/50 transition-colors" onClick={() => shiftDate(-1)} title="Previous day">
<ChevronLeft size={16} /> <ChevronLeft size={16} />
</button> </button>
<div className="relative flex items-center gap-2 px-3 py-1"> <div className="relative flex items-center gap-1.5 sm:gap-2 px-2 sm:px-3 py-0.5 sm:py-1">
<Calendar size={14} className="text-slate-400" /> <Calendar size={13} className="text-slate-400 hidden sm:block" />
<div className="flex flex-col"> <div className="flex flex-col">
<span className="text-[9px] font-bold text-slate-400 uppercase tracking-widest leading-none mb-0.5 flex items-center gap-1"> <span className="text-[8px] sm:text-[9px] font-bold text-slate-400 uppercase tracking-widest leading-none mb-0.5 flex items-center gap-1">
Date {isToday && <span className="text-[8px] bg-blue-100 text-blue-600 px-1 rounded-sm">TODAY</span>} Date {isToday && <span className="text-[7px] sm:text-[8px] bg-blue-100 text-blue-600 px-1 rounded-sm">TODAY</span>}
</span> </span>
<span className="text-xs font-bold text-slate-700 leading-none">{prettyDate}</span> <span className="text-[11px] sm:text-xs font-bold text-slate-700 leading-none">{prettyDate}</span>
</div> </div>
<input <input
type="date" type="date"
@@ -102,7 +102,7 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
aria-label="Pick date" aria-label="Pick date"
/> />
</div> </div>
<button className="p-1.5 rounded-lg text-slate-400 hover:text-slate-700 hover:bg-slate-200/50 transition-colors disabled:opacity-30 disabled:hover:bg-transparent" onClick={() => shiftDate(1)} disabled={isToday} title="Next day"> <button className="p-1 sm:p-1.5 rounded-lg text-slate-400 hover:text-slate-700 hover:bg-slate-200/50 transition-colors disabled:opacity-30 disabled:hover:bg-transparent" onClick={() => shiftDate(1)} disabled={isToday} title="Next day">
<ChevronRight size={16} /> <ChevronRight size={16} />
</button> </button>
</div> </div>
@@ -110,11 +110,11 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
</div> </div>
{/* ── Unified Tab Row ── */} {/* ── Unified Tab Row ── */}
<div className="flex items-center px-6 py-3 border-b border-slate-200 shadow-sm z-10 relative"> <div className="flex items-center px-3 sm:px-6 py-2.5 border-b border-slate-200 shadow-sm z-10 relative overflow-x-auto custom-scrollbar no-scrollbar touch-scrolling">
<div className="flex items-center p-1 bg-slate-100/80 rounded-lg border border-slate-200/80 shadow-inner"> <div className="flex items-center p-1 bg-slate-100/80 rounded-lg border border-slate-200/80 shadow-inner shrink-0">
<button <button
onClick={() => setActiveTab('map')} onClick={() => setActiveTab('map')}
className={`flex items-center gap-1.5 px-4 py-1.5 text-[11px] font-extrabold rounded-md transition-all duration-300 uppercase tracking-wide ${ className={`flex items-center gap-1.5 px-3 sm:px-4 py-1 sm:py-1.5 text-[10px] sm:text-[11px] font-extrabold rounded-md transition-all duration-300 uppercase tracking-wide ${
activeTab === 'map' ? 'bg-white text-purple-700 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50' activeTab === 'map' ? 'bg-white text-purple-700 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50'
}`} }`}
> >
@@ -123,7 +123,7 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
<button <button
onClick={() => setActiveTab('orders')} onClick={() => setActiveTab('orders')}
className={`flex items-center gap-1.5 px-4 py-1.5 text-[11px] font-extrabold rounded-md transition-all duration-300 uppercase tracking-wide ${ className={`flex items-center gap-1.5 px-3 sm:px-4 py-1 sm:py-1.5 text-[10px] sm:text-[11px] font-extrabold rounded-md transition-all duration-300 uppercase tracking-wide ${
activeTab === 'orders' ? 'bg-white text-purple-700 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50' activeTab === 'orders' ? 'bg-white text-purple-700 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50'
}`} }`}
> >
@@ -132,7 +132,7 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
<button <button
onClick={() => setActiveTab('deliveries')} onClick={() => setActiveTab('deliveries')}
className={`flex items-center gap-1.5 px-4 py-1.5 text-[11px] font-extrabold rounded-md transition-all duration-300 uppercase tracking-wide ${ className={`flex items-center gap-1.5 px-3 sm:px-4 py-1 sm:py-1.5 text-[10px] sm:text-[11px] font-extrabold rounded-md transition-all duration-300 uppercase tracking-wide ${
activeTab === 'deliveries' ? 'bg-white text-purple-700 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50' activeTab === 'deliveries' ? 'bg-white text-purple-700 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50'
}`} }`}
> >
@@ -141,10 +141,10 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
</div> </div>
{/* Sub-tabs for Map View */} {/* Sub-tabs for Map View */}
<div className="flex items-center p-1 ml-4 bg-slate-100/80 rounded-lg border border-slate-200/80 shadow-inner"> <div className="flex items-center p-1 ml-3 sm:ml-4 bg-slate-100/80 rounded-lg border border-slate-200/80 shadow-inner shrink-0">
<button <button
onClick={() => setMapViewMode('stores')} onClick={() => setMapViewMode('stores')}
className={`flex items-center gap-1.5 px-4 py-1.5 text-[11px] font-extrabold rounded-md transition-all duration-300 tracking-wide ${ className={`flex items-center gap-1.5 px-3 sm:px-4 py-1 sm:py-1.5 text-[10px] sm:text-[11px] font-extrabold rounded-md transition-all duration-300 tracking-wide ${
mapViewMode === 'stores' ? 'bg-white text-slate-800 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50' mapViewMode === 'stores' ? 'bg-white text-slate-800 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50'
}`} }`}
> >
@@ -153,7 +153,7 @@ export default function DispatchHubView({ locationid, tenantId = FIESTA_TENANT_I
<button <button
onClick={() => setMapViewMode('customers')} onClick={() => setMapViewMode('customers')}
className={`flex items-center gap-1.5 px-4 py-1.5 text-[11px] font-extrabold rounded-md transition-all duration-300 tracking-wide ${ className={`flex items-center gap-1.5 px-3 sm:px-4 py-1 sm:py-1.5 text-[10px] sm:text-[11px] font-extrabold rounded-md transition-all duration-300 tracking-wide ${
mapViewMode === 'customers' ? 'bg-white text-slate-800 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50' mapViewMode === 'customers' ? 'bg-white text-slate-800 shadow-sm ring-1 ring-slate-900/5' : 'text-slate-500 hover:text-slate-800 hover:bg-slate-200/50'
}`} }`}
> >

View File

@@ -11063,3 +11063,24 @@
height: 100%; height: 100%;
transition: width 0.3s, background 0.3s; transition: width 0.3s, background 0.3s;
} }
/* Mobile & Tablet Responsiveness for Dispatch Cockpit */
@media (max-width: 1024px) {
.dispatch-container #body {
flex-direction: column !important;
overflow-y: auto !important;
}
.dispatch-container #sidebar {
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
height: auto !important;
border-right: none !important;
border-bottom: 1px solid var(--border) !important;
}
.dispatch-container #map-pane {
height: 350px !important;
min-height: 350px !important;
width: 100% !important;
}
}

View File

@@ -72,19 +72,19 @@ export default function Header({
return ( return (
<header className="bg-[#662582] flex justify-between items-center w-full px-container-margin py-md fixed top-0 right-0 left-0 z-50 h-16 text-white"> <header className="bg-[#662582] flex justify-between items-center w-full px-container-margin py-md fixed top-0 right-0 left-0 z-50 h-16 text-white">
{/* Brand & Desktop Navigation Tabs */} {/* Brand & Desktop Navigation Tabs */}
<div className="flex items-center gap-md md:pl-0 pl-1"> <div className="flex items-center gap-sm sm:gap-md md:pl-0 pl-1 min-w-0">
{/* Brand cell — width mirrors the sidebar rail (64px collapsed / 256px expanded) so the logo/toggle sit directly above it */} {/* Brand cell — width mirrors the sidebar rail (64px collapsed / 256px expanded) so the logo/toggle sit directly above it */}
<div <div
className={`flex items-center shrink-0 transition-all duration-300 ${ className={`flex items-center shrink-0 transition-all duration-300 ${
isSidebarOpen ? 'gap-md md:w-64' : 'gap-sm md:w-16 md:justify-center' isSidebarOpen ? 'gap-sm sm:gap-md md:w-64' : 'gap-sm md:w-16 md:justify-center'
}`} }`}
> >
{/* Brand Logo — full wordmark when sidebar open, icon only when collapsed */} {/* Brand Logo — full wordmark when sidebar open, icon only when collapsed */}
<span className="select-none flex items-center shrink-0 -ml-1.5"> <span className="select-none flex items-center shrink-0 -ml-1">
<img <img
src={isSidebarOpen ? '/logo.png' : '/favicon.png'} src={isSidebarOpen ? '/logo.png' : '/favicon.png'}
alt="nearledaily logo" alt="nearledaily logo"
className={`w-auto object-contain transition-all duration-300 ${isSidebarOpen ? 'h-7' : 'h-9'}`} className={`w-auto object-contain transition-all duration-300 ${isSidebarOpen ? 'h-6 sm:h-7' : 'h-8 sm:h-9'}`}
/> />
</span> </span>
@@ -92,7 +92,7 @@ export default function Header({
<button <button
onClick={onToggleSidebar} onClick={onToggleSidebar}
title="Toggle sidebar" title="Toggle sidebar"
className={`rounded-full hover:bg-purple-800 transition-colors cursor-pointer text-white ${isSidebarOpen ? 'p-2' : 'p-1.5'}`} className={`rounded-full hover:bg-purple-800 transition-colors cursor-pointer text-white ${isSidebarOpen ? 'p-1.5 sm:p-2' : 'p-1.5'}`}
> >
<Menu size={18} /> <Menu size={18} />
</button> </button>
@@ -100,13 +100,13 @@ export default function Header({
{/* Dynamic Store Name Context */} {/* Dynamic Store Name Context */}
{storeContext && ( {storeContext && (
<div className="flex flex-col ml-1 border-l border-[#662582] pl-3 py-0.5 select-none"> <div className="flex flex-col ml-0.5 sm:ml-1 border-l border-white/20 pl-2 sm:pl-3 py-0.5 select-none min-w-0">
<span className="flex items-center gap-2 text-lg font-bold tracking-tight text-white leading-tight"> <span className="flex items-center gap-1.5 sm:gap-2 text-xs sm:text-base md:text-lg font-bold tracking-tight text-white leading-tight truncate max-w-[110px] xs:max-w-[150px] sm:max-w-none">
{storeContext.icon && <storeContext.icon size={20} className="text-purple-300" />} {storeContext.icon && <storeContext.icon size={16} className="text-purple-300 shrink-0 hidden xs:inline" />}
{storeContext.storeName} <span className="truncate">{storeContext.storeName}</span>
</span> </span>
{storeContext.branchName && ( {storeContext.branchName && (
<span className="text-[10px] font-medium text-purple-200 tracking-wider uppercase leading-tight"> <span className="text-[9px] sm:text-[10px] font-medium text-purple-200 tracking-wider uppercase leading-tight truncate max-w-[100px] sm:max-w-none">
{storeContext.branchName} {storeContext.branchName}
</span> </span>
)} )}

View File

@@ -483,7 +483,7 @@ export default function InventoryView({
<p className="text-[9px] text-zinc-400 font-medium mt-1 truncate">Pick products & set quantities selected items appear in every store's catalogue.</p> <p className="text-[9px] text-zinc-400 font-medium mt-1 truncate">Pick products & set quantities selected items appear in every store's catalogue.</p>
</div> </div>
<div className="flex flex-nowrap items-center gap-2 shrink-0"> <div className="flex flex-wrap items-center gap-2 w-full sm:w-auto shrink-0">
<button <button
onClick={() => setIsLocalSidebarOpen(!isLocalSidebarOpen)} onClick={() => setIsLocalSidebarOpen(!isLocalSidebarOpen)}
className="flex items-center gap-1.5 bg-white border border-slate-200 px-2.5 py-1.5 rounded-lg text-[10px] font-bold hover:bg-slate-50 transition-colors text-slate-700 shadow-sm cursor-pointer h-[32px] whitespace-nowrap" className="flex items-center gap-1.5 bg-white border border-slate-200 px-2.5 py-1.5 rounded-lg text-[10px] font-bold hover:bg-slate-50 transition-colors text-slate-700 shadow-sm cursor-pointer h-[32px] whitespace-nowrap"
@@ -491,7 +491,7 @@ export default function InventoryView({
<Filter size={12} /> Filter <Filter size={12} /> Filter
</button> </button>
<div className="relative w-32 xl:w-48 h-[32px]"> <div className="relative flex-1 sm:flex-none w-full sm:w-32 xl:w-48 h-[32px]">
<input <input
type="text" type="text"
placeholder="Search..." placeholder="Search..."

View File

@@ -80,95 +80,38 @@ export default function LoginView({ onLogin }: LoginViewProps) {
return ( return (
<div className="h-screen w-full flex bg-white font-sans text-slate-800 overflow-hidden"> <div className="h-screen w-full flex bg-white font-sans text-slate-800 overflow-hidden">
{/* ── Left brand / hero panel (desktop only) ── */} {/* ── Left brand / hero panel (desktop only) ── */}
<div className="hidden lg:flex lg:w-1/2 shrink-0 relative overflow-hidden bg-gradient-to-br from-[#662582] via-[#4c1d66] to-slate-950 text-white flex-col p-12 xl:p-16 justify-center"> <div className="hidden lg:flex lg:w-1/2 shrink-0 relative overflow-hidden bg-gradient-to-br from-[#531b6e] via-[#3d1653] to-slate-950 text-white flex-col p-8 xl:p-14 2xl:p-20 justify-center items-center">
{/* Layered ambient glows */} {/* Layered ambient glows */}
<div className="absolute -top-24 -right-24 w-96 h-96 bg-purple-500/25 rounded-full blur-[120px] pointer-events-none" /> <div className="absolute -top-24 -right-24 w-96 h-96 bg-purple-500/20 rounded-full blur-[120px] pointer-events-none" />
<div className="absolute -bottom-24 -left-24 w-[26rem] h-[26rem] bg-indigo-500/20 rounded-full blur-[140px] pointer-events-none animate-pulse" style={{ animationDuration: '9s' }} /> <div className="absolute -bottom-24 -left-24 w-[24rem] h-[24rem] bg-indigo-500/15 rounded-full blur-[140px] pointer-events-none" />
<div className="absolute top-1/3 left-1/2 w-72 h-72 bg-fuchsia-500/10 rounded-full blur-[120px] pointer-events-none" />
{/* Subtle dot-grid texture */} {/* Subtle dot-grid texture */}
<div <div
className="absolute inset-0 opacity-[0.07] pointer-events-none" className="absolute inset-0 opacity-[0.05] pointer-events-none"
style={{ backgroundImage: 'radial-gradient(circle, #ffffff 1px, transparent 1px)', backgroundSize: '22px 22px' }} style={{ backgroundImage: 'radial-gradient(circle, #ffffff 1px, transparent 1px)', backgroundSize: '24px 24px' }}
/> />
<div className="relative z-10 flex flex-col justify-center w-full max-w-[520px] mx-auto h-full"> <div className="relative z-10 flex flex-col justify-center w-full max-w-[480px]">
{/* Logo */} {/* Logo */}
<div className="shrink-0 mb-10"> <div className="mb-8">
<img src="/logo.png" alt="nearledaily" className="h-10 w-auto object-contain" /> <img src="/logo.png" alt="nearledaily" className="h-10 xl:h-12 w-auto object-contain" />
</div> </div>
{/* Hero */} {/* Eyebrow badge */}
<div className="min-h-0"> <div className="inline-flex items-center gap-1.5 text-xs font-semibold tracking-wider text-purple-200 bg-white/10 border border-white/15 rounded-full px-3.5 py-1.5 backdrop-blur-md self-start">
{/* Eyebrow */} <ShieldCheck size={14} className="text-purple-300 shrink-0" />
<span className="inline-flex items-center gap-1.5 self-start text-[11px] font-bold uppercase tracking-[0.15em] text-purple-100 bg-white/10 border border-white/15 rounded-full px-3 py-1 backdrop-blur-sm"> <span>Secure Merchant Console</span>
<ShieldCheck size={13} className="text-purple-200" /> </div>
Secure Merchant Console
</span>
<h1 <h1 className="text-4xl lg:text-5xl xl:text-6xl font-extrabold tracking-tight leading-[1.1] mt-6">
className="text-5xl xl:text-6xl font-bold tracking-tight leading-[1.1] mt-6 max-w-[460px]" All Operations <br />
style={{ textShadow: '0 2px 16px rgba(0,0,0,0.25)' }} <span className="bg-gradient-to-r from-white via-purple-100 to-purple-300 bg-clip-text text-transparent">
>
All Operations
<br />
In One Console In One Console
</span>
</h1> </h1>
<p className="text-purple-100/90 text-lg xl:text-xl leading-[1.6] mt-6 max-w-[460px]">
<p className="text-purple-200/90 text-base xl:text-lg leading-relaxed mt-5 max-w-[460px]">
Monitor regional hubs, track live order operations, and manage your entire store network from a single command center. Monitor regional hubs, track live order operations, and manage your entire store network from a single command center.
</p> </p>
{/* Glassmorphic live-snapshot preview card */}
<div className="mt-10 max-w-[460px] rounded-2xl bg-white/10 backdrop-blur-md border border-white/15 p-6 shadow-2xl shadow-purple-950/50">
<div className="flex items-center justify-between">
<span className="text-xs font-bold uppercase tracking-widest text-purple-100">Live Operations</span>
<span className="inline-flex items-center gap-1.5 text-xs font-bold text-emerald-300">
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse" />
LIVE
</span>
</div>
<div className="mt-5 grid grid-cols-2 gap-6">
<div>
<p className="text-3xl font-extrabold tracking-tight tabular-nums">2,480</p>
<p className="text-xs text-purple-200 mt-1">Orders today</p>
</div>
<div>
<p className="text-3xl font-extrabold tracking-tight text-emerald-300 tabular-nums">98.6%</p>
<p className="text-xs text-purple-200 mt-1">Fulfillment</p>
</div>
</div>
{/* Mini bar chart */}
<div className="mt-5 flex items-end gap-1.5 h-14">
{[45, 62, 38, 70, 52, 80, 60, 90, 55, 74].map((h, i) => (
<div
key={i}
className="flex-1 rounded-t bg-gradient-to-t from-purple-500/40 to-purple-200/80"
style={{ height: `${h}%` }}
/>
))}
</div>
</div>
</div>
{/* Trust stats + copyright */}
<div className="shrink-0 mt-10 pt-6 border-t border-white/10 max-w-[460px]">
<div className="grid grid-cols-3 gap-4">
{[
{ v: '120+', l: 'Regional hubs' },
{ v: '99.9%', l: 'Platform uptime' },
{ v: '24/7', l: 'Live monitoring' },
].map((s) => (
<div key={s.l}>
<p className="text-2xl font-bold tracking-tight">{s.v}</p>
<p className="text-xs text-purple-300 mt-0.5">{s.l}</p>
</div>
))}
</div>
<p className="text-xs text-purple-300/70 font-medium mt-4">
© 2026 nearledaily · All rights reserved
</p>
</div>
</div> </div>
</div> </div>

View File

@@ -339,3 +339,15 @@
from { opacity: 0; transform: translateY(20px) scale(0.96); } from { opacity: 0; transform: translateY(20px) scale(0.96); }
to { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 1; transform: translateY(0) scale(1); }
} }
@media (max-width: 640px) {
.odm-overlay {
padding: 12px !important;
}
.odm-overlay .odm-card {
max-width: 100% !important;
max-height: 92vh !important;
border-radius: 14px !important;
}
}

View File

@@ -322,7 +322,7 @@ export default function OrdersView({ searchQuery = '', locationid, tenantId = FI
{/* Status tabs + search */} {/* Status tabs + search */}
<FilterBar className="mb-4"> <FilterBar className="mb-4">
<div className="flex flex-col lg:flex-row lg:items-center gap-3"> <div className="flex flex-col lg:flex-row lg:items-center gap-3">
<div className="flex items-center gap-2 overflow-x-auto py-0.5 flex-1 min-w-0"> <div className="flex items-center gap-2 overflow-x-auto custom-scrollbar no-scrollbar py-0.5 flex-1 min-w-0 touch-scrolling">
{STATUS_TABS.map((t) => { {STATUS_TABS.map((t) => {
// Single brand accent for the tab row (calmer than per-status colours); // Single brand accent for the tab row (calmer than per-status colours);
// the per-status hue still appears on the row Status chip where it aids scanning. // the per-status hue still appears on the row Status chip where it aids scanning.

View File

@@ -471,7 +471,8 @@ export default function PosView({ locationid, tenantId = FIESTA_TENANT_ID }: Pos
</button> </button>
</div> </div>
) : ( ) : (
<table className="w-full text-left border-collapse"> <div className="w-full overflow-x-auto custom-scrollbar">
<table className="w-full text-left border-collapse min-w-[500px]">
<thead className="sticky top-0 z-10 bg-slate-50 border-b border-slate-200"> <thead className="sticky top-0 z-10 bg-slate-50 border-b border-slate-200">
<tr> <tr>
<th className="px-5 py-3 text-[11px] font-bold uppercase tracking-wider text-slate-500">Product</th> <th className="px-5 py-3 text-[11px] font-bold uppercase tracking-wider text-slate-500">Product</th>
@@ -604,6 +605,7 @@ export default function PosView({ locationid, tenantId = FIESTA_TENANT_ID }: Pos
))} ))}
</tbody> </tbody>
</table> </table>
</div>
)} )}
</div> </div>

View File

@@ -287,7 +287,7 @@ export default function SettingsView({ tenantId = FIESTA_TENANT_ID, user }: Sett
</div> </div>
{/* Navigation tab rail */} {/* Navigation tab rail */}
<nav className="flex lg:flex-col gap-1.5 overflow-x-auto select-none"> <nav className="flex lg:flex-col gap-1.5 overflow-x-auto custom-scrollbar no-scrollbar touch-scrolling select-none">
{tabs.map((t) => { {tabs.map((t) => {
const Icon = t.icon; const Icon = t.icon;
const active = activeTab === t.key; const active = activeTab === t.key;

View File

@@ -67,6 +67,11 @@ export default function Sidebar({
key={item.id} key={item.id}
to={`/admin/${item.id}`} to={`/admin/${item.id}`}
title={item.label} title={item.label}
onClick={() => {
if (window.innerWidth < 768) {
onClose();
}
}}
className={({ isActive }) => `w-full flex items-center py-3 rounded-lg text-left transition-all duration-200 cursor-pointer ${ className={({ isActive }) => `w-full flex items-center py-3 rounded-lg text-left transition-all duration-200 cursor-pointer ${
isOpen ? 'gap-md px-md border-l-4' : 'justify-center px-0' isOpen ? 'gap-md px-md border-l-4' : 'justify-center px-0'
} ${ } ${

View File

@@ -51,6 +51,11 @@ export default function UserStoreSidebar({ items, isOpen, onClose }: UserStoreSi
key={item.id} key={item.id}
to={`/store/${item.id}`} to={`/store/${item.id}`}
title={item.label} title={item.label}
onClick={() => {
if (window.innerWidth < 768) {
onClose();
}
}}
className={({ isActive }) => `w-full flex items-center py-3 rounded-lg text-left transition-all duration-200 cursor-pointer ${ className={({ isActive }) => `w-full flex items-center py-3 rounded-lg text-left transition-all duration-200 cursor-pointer ${
isOpen ? 'gap-md px-md border-l-4' : 'justify-center px-0' isOpen ? 'gap-md px-md border-l-4' : 'justify-center px-0'
} ${ } ${

View File

@@ -103,4 +103,15 @@
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: var(--color-slate-350, #cbd5e1) transparent; scrollbar-color: var(--color-slate-350, #cbd5e1) transparent;
} }
.no-scrollbar::-webkit-scrollbar {
display: none;
} }
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.touch-scrolling {
-webkit-overflow-scrolling: touch;
}
}

View File

@@ -31,7 +31,7 @@ import { firstRow, num, str, type Row } from './fiestaApi';
*/ */
const LOGIN_ENDPOINT = import.meta.env.VITE_FIESTA_URL const LOGIN_ENDPOINT = import.meta.env.VITE_FIESTA_URL
? `${import.meta.env.VITE_FIESTA_URL}/users/applogin` ? `${import.meta.env.VITE_FIESTA_URL}/users/applogin`
: 'http://localhost:1122/live/api/v1/web/users/applogin'; : 'https://fiesta.nearle.app/live/api/v1/web/users/applogin';
/** Request body field names the endpoint expects for the credentials. */ /** Request body field names the endpoint expects for the credentials. */
const REQUEST_FIELDS = { const REQUEST_FIELDS = {