udpates on the ui changes on theprodut catalogs
This commit is contained in:
117
src/App.tsx
117
src/App.tsx
@@ -25,13 +25,19 @@ import {
|
||||
Phone,
|
||||
Activity,
|
||||
TrendingUp,
|
||||
Award
|
||||
Award,
|
||||
Layers,
|
||||
Store,
|
||||
Settings,
|
||||
LayoutDashboard
|
||||
} from 'lucide-react';
|
||||
|
||||
import { MainSection } from './types';
|
||||
import {
|
||||
useFiestaTenantLocations,
|
||||
useFiestaLocationSummary,
|
||||
useFiestaUpdateLocation,
|
||||
useFiestaOrderSummary,
|
||||
} from './services/fiestaQueries';
|
||||
import { FIESTA_TENANT_ID, str as fstr } from './services/fiestaApi';
|
||||
import Sidebar from './components/Sidebar';
|
||||
@@ -113,6 +119,15 @@ export default function App() {
|
||||
// under Settings → Users & Access (see UsersPanel).
|
||||
const locationsQ = useFiestaTenantLocations(tenantId);
|
||||
const locSummaryQ = useFiestaLocationSummary(tenantId);
|
||||
const updateLocation = useFiestaUpdateLocation();
|
||||
|
||||
const today = new Date();
|
||||
const monthStart = new Date(today);
|
||||
monthStart.setDate(today.getDate() - 30);
|
||||
const ymd = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
const fromdate = ymd(monthStart);
|
||||
const todate = ymd(today);
|
||||
const summaryQ = useFiestaOrderSummary(tenantId, fromdate, todate);
|
||||
|
||||
const STORE_COVERS = [
|
||||
'https://images.unsplash.com/photo-1542838132-92c53300491e?auto=format&fit=crop&w=600&q=80',
|
||||
@@ -143,7 +158,7 @@ export default function App() {
|
||||
const [storesList, setStoresList] = useState<Array<{ locationid?: number; name: string; zone: string; deliveries: number; sales: string; orders: number; staff: string; color: string; status: string }>>([]);
|
||||
|
||||
const [storesSearch, setStoresSearch] = useState('');
|
||||
const [storesFilter, setStoresFilter] = useState<'ALL' | 'ACTIVE' | 'CRITICAL'>('ALL');
|
||||
const [storesFilter, setStoresFilter] = useState<'ALL' | 'ACTIVE' | 'INACTIVE'>('ALL');
|
||||
|
||||
const filteredStoresList = storesList.filter((st) => {
|
||||
const q = storesSearch.trim().toLowerCase();
|
||||
@@ -158,10 +173,10 @@ export default function App() {
|
||||
if (storesFilter === 'ACTIVE') {
|
||||
return matchesSearch && st.status.toLowerCase() === 'active';
|
||||
}
|
||||
if (storesFilter === 'CRITICAL') {
|
||||
if (storesFilter === 'INACTIVE') {
|
||||
return (
|
||||
matchesSearch &&
|
||||
(st.status.toLowerCase() !== 'active' || st.deliveries > 40)
|
||||
(st.status.toLowerCase() !== 'active')
|
||||
);
|
||||
}
|
||||
return matchesSearch;
|
||||
@@ -257,13 +272,6 @@ export default function App() {
|
||||
This merchant operates a single store. Add a branch to manage multiple outlets.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAddStoreModal(true)}
|
||||
className="bg-[#581c87] text-white px-5 py-2.5 rounded-lg text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 cursor-pointer hover:bg-purple-800 transition shadow-sm self-start md:self-auto"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Add Retail Outlet Node
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<StoreDetailView
|
||||
@@ -287,14 +295,6 @@ export default function App() {
|
||||
Local nodes registry, active manager assignments, and live dispatch and grocery delivery fulfillment statistics.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setShowAddStoreModal(true)}
|
||||
className="bg-[#581c87] text-white px-5 py-2.5 rounded-lg text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 cursor-pointer hover:bg-purple-800 transition shadow-sm"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Add Retail Outlet Node
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Filter control bar */}
|
||||
@@ -309,7 +309,7 @@ export default function App() {
|
||||
placeholder="Search stores by name, zone, or manager..."
|
||||
value={storesSearch}
|
||||
onChange={(e) => setStoresSearch(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 bg-zinc-50 border border-zinc-200 rounded-lg text-xs font-medium text-zinc-800 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-[#581c87]/20 focus:border-[#581c87] transition-all"
|
||||
className="w-full pl-10 pr-4 py-2 bg-zinc-50 border border-zinc-200 rounded-lg text-xs font-medium text-zinc-800 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-[#662582]/20 focus:border-[#662582] transition-all"
|
||||
/>
|
||||
{storesSearch && (
|
||||
<button
|
||||
@@ -327,7 +327,7 @@ export default function App() {
|
||||
onClick={() => setStoresFilter('ALL')}
|
||||
className={`flex-1 md:flex-none px-4 py-1.5 rounded-md text-xs font-bold transition-all ${
|
||||
storesFilter === 'ALL'
|
||||
? 'bg-white text-[#581c87] shadow-sm'
|
||||
? 'bg-white text-[#662582] shadow-sm'
|
||||
: 'text-zinc-500 hover:text-zinc-800 hover:bg-white/40'
|
||||
}`}
|
||||
>
|
||||
@@ -344,14 +344,14 @@ export default function App() {
|
||||
Active ({storesList.filter(s => s.status.toLowerCase() === 'active').length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setStoresFilter('CRITICAL')}
|
||||
onClick={() => setStoresFilter('INACTIVE')}
|
||||
className={`flex-1 md:flex-none px-4 py-1.5 rounded-md text-xs font-bold transition-all ${
|
||||
storesFilter === 'CRITICAL'
|
||||
storesFilter === 'INACTIVE'
|
||||
? 'bg-white text-rose-600 shadow-sm'
|
||||
: 'text-zinc-500 hover:text-rose-650 hover:bg-white/40'
|
||||
}`}
|
||||
>
|
||||
Critical Alerts ({storesList.filter(s => s.status.toLowerCase() !== 'active' || s.deliveries > 40 || s.deliveries === 0).length})
|
||||
Inactive ({storesList.filter(s => s.status.toLowerCase() !== 'active').length})
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -361,7 +361,7 @@ export default function App() {
|
||||
<div className="text-center py-12 text-zinc-400 text-xs border border-dashed border-[#e2e8f0] rounded-xl bg-white p-8">
|
||||
{locationsQ.isLoading ? (
|
||||
<div className="flex flex-col items-center justify-center gap-2">
|
||||
<div className="w-6 h-6 border-2 border-[#581c87] border-t-transparent rounded-full animate-spin" />
|
||||
<div className="w-6 h-6 border-2 border-[#662582] border-t-transparent rounded-full animate-spin" />
|
||||
<span>Loading live store locations…</span>
|
||||
</div>
|
||||
) : (
|
||||
@@ -435,7 +435,7 @@ export default function App() {
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-[9px] text-zinc-400 uppercase tracking-widest font-bold block">Total Orders</span>
|
||||
<p className="font-extrabold text-base text-[#581c87] mt-0.5 font-mono">{totalOrders.toLocaleString()}</p>
|
||||
<p className="font-extrabold text-base text-[#662582] mt-0.5 font-mono">{totalOrders.toLocaleString()}</p>
|
||||
<span className="text-[8px] text-purple-600 font-semibold block mt-0.5">Incoming Volume</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -479,7 +479,7 @@ export default function App() {
|
||||
<div className="mt-4 pt-3.5 border-t border-zinc-100">
|
||||
<div className="flex justify-between items-center text-[8px] text-zinc-400 font-bold uppercase tracking-widest mb-1.5">
|
||||
<span>Speed Index (Live Feed)</span>
|
||||
<span className="text-[#581c87] flex items-center gap-0.5 text-[8px] font-bold">
|
||||
<span className="text-[#662582] flex items-center gap-0.5 text-[8px] font-bold">
|
||||
<Activity className="w-2.5 h-2.5 animate-pulse" /> Live
|
||||
</span>
|
||||
</div>
|
||||
@@ -492,7 +492,7 @@ export default function App() {
|
||||
st.status.toLowerCase() === 'active'
|
||||
? st.deliveries > 40
|
||||
? 'bg-rose-500/80 group-hover/bar:bg-rose-500'
|
||||
: 'bg-[#581c87]/70 group-hover/bar:bg-[#581c87]'
|
||||
: 'bg-[#662582]/70 group-hover/bar:bg-[#662582]'
|
||||
: 'bg-amber-500/70 group-hover/bar:bg-amber-500'
|
||||
}`}
|
||||
/>
|
||||
@@ -518,7 +518,7 @@ export default function App() {
|
||||
e.stopPropagation();
|
||||
alert(`Routing communications channel directly to manager ${st.staff}...`);
|
||||
}}
|
||||
className="p-1.5 rounded-lg bg-white border border-zinc-200 hover:border-[#581c87] hover:text-[#581c87] text-zinc-500 transition-colors shadow-sm"
|
||||
className="p-1.5 rounded-lg bg-white border border-zinc-200 hover:border-[#662582] hover:text-[#662582] text-zinc-500 transition-colors shadow-sm"
|
||||
title="Communicate with Node Lead"
|
||||
>
|
||||
<Phone className="w-3.5 h-3.5" />
|
||||
@@ -526,9 +526,26 @@ export default function App() {
|
||||
</div>
|
||||
|
||||
{/* Card footer - enter console */}
|
||||
<div className="flex items-center justify-between text-[10px] font-bold text-[#581c87] mt-4 pt-3 border-t border-zinc-100/80">
|
||||
<span className="uppercase tracking-wider">Enter Terminal Console</span>
|
||||
<ArrowRight className="w-3.5 h-3.5 transform group-hover:translate-x-1.5 transition-transform duration-350 text-[#581c87]" />
|
||||
<div className="flex items-center justify-between text-[10px] font-bold text-[#662582] mt-4 pt-3 border-t border-zinc-100/80">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const newStatus = st.status.toLowerCase() === 'active' ? 'Inactive' : 'Active';
|
||||
setStoresList(stores => stores.map(s => s.locationid === st.locationid ? { ...s, status: newStatus } : s));
|
||||
if (st.locationid) {
|
||||
updateLocation.mutate({ locationid: st.locationid, status: newStatus.toLowerCase() });
|
||||
}
|
||||
}}
|
||||
className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus:outline-none ${st.status.toLowerCase() === 'active' ? 'bg-emerald-500' : 'bg-zinc-300'}`}
|
||||
title={`Toggle store to ${st.status.toLowerCase() === 'active' ? 'Inactive' : 'Active'}`}
|
||||
>
|
||||
<span className={`inline-block h-3 w-3 transform rounded-full bg-white transition-transform ${st.status.toLowerCase() === 'active' ? 'translate-x-5' : 'translate-x-1'}`} />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-1 uppercase tracking-wider">
|
||||
<span>Enter Terminal Console</span>
|
||||
<ArrowRight className="w-3.5 h-3.5 transform group-hover:translate-x-1.5 transition-transform duration-350" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -571,10 +588,28 @@ export default function App() {
|
||||
onHelpClick={handleHelp}
|
||||
onLogoutClick={handleLogout}
|
||||
profile={currentUser}
|
||||
storeContext={{
|
||||
storeName: currentSection === 'dashboard'
|
||||
? (summaryQ.data?.tenantname ? `${summaryQ.data.tenantname} Admin` : 'Admin Console')
|
||||
: currentSection === 'inventory'
|
||||
? 'Products'
|
||||
: currentSection.charAt(0).toUpperCase() + currentSection.slice(1),
|
||||
icon: currentSection === 'dashboard'
|
||||
? LayoutDashboard
|
||||
: currentSection === 'inventory'
|
||||
? Layers
|
||||
: currentSection === 'stores'
|
||||
? Store
|
||||
: currentSection === 'reports'
|
||||
? TrendingUp
|
||||
: currentSection === 'settings'
|
||||
? Settings
|
||||
: undefined
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Main Container workspace layout splits */}
|
||||
<div className="flex pt-20">
|
||||
<div className="flex pt-16">
|
||||
|
||||
{/* Interactive Left Rail */}
|
||||
<Sidebar
|
||||
@@ -587,7 +622,7 @@ export default function App() {
|
||||
/>
|
||||
|
||||
{/* Main core pages payload area */}
|
||||
<main className={`flex-1 min-w-0 min-h-[calc(100vh-80px)] transition-all duration-300 ${sidebarOpen ? 'md:pl-64' : 'md:pl-20'}`}>
|
||||
<main className={`flex-1 min-w-0 min-h-[calc(100vh-64px)] transition-all duration-300 ${sidebarOpen ? 'md:pl-64' : 'md:pl-20'}`}>
|
||||
<div className="w-full p-container-margin md:p-xl space-y-lg transition-all duration-300">
|
||||
{/* Nav content routing */}
|
||||
{currentSection === 'dashboard' && (
|
||||
@@ -629,7 +664,7 @@ export default function App() {
|
||||
<div className="bg-white border border-[#e2e8f0] rounded-xl w-full max-w-[24rem] max-h-[90vh] flex flex-col shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200 text-xs font-sans cursor-default">
|
||||
<div className="p-md border-b border-[#e2e8f0] bg-[#f8fafc] flex justify-between items-center shrink-0">
|
||||
<h4 className="font-bold text-[#0f172a] flex items-center gap-xs">
|
||||
<Calendar size={15} className="text-[#581c87]" />
|
||||
<Calendar size={15} className="text-[#662582]" />
|
||||
Scheduled Reports Calendar
|
||||
</h4>
|
||||
<button
|
||||
@@ -669,7 +704,7 @@ export default function App() {
|
||||
<div className="bg-white border border-[#e2e8f0] rounded-xl w-full max-w-[24rem] max-h-[90vh] flex flex-col shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200 text-xs font-sans cursor-default">
|
||||
<div className="p-md border-b border-[#e2e8f0] bg-[#f8fafc] flex justify-between items-center shrink-0">
|
||||
<h4 className="font-bold text-[#0f172a] flex items-center gap-xs">
|
||||
<Building size={15} className="text-[#581c87]" />
|
||||
<Building size={15} className="text-[#662582]" />
|
||||
Commission New Regional Store Node
|
||||
</h4>
|
||||
<button
|
||||
@@ -690,7 +725,7 @@ export default function App() {
|
||||
placeholder="e.g. RS Puram Super Hub"
|
||||
value={newStore.name}
|
||||
onChange={(e) => setNewStore({ ...newStore, name: e.target.value })}
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#581c87]"
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#662582]"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -702,7 +737,7 @@ export default function App() {
|
||||
placeholder="e.g. Coimbatore North"
|
||||
value={newStore.zone}
|
||||
onChange={(e) => setNewStore({ ...newStore, zone: e.target.value })}
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#581c87]"
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#662582]"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -714,7 +749,7 @@ export default function App() {
|
||||
placeholder="e.g. Sridhar Sundaram"
|
||||
value={newStore.lead}
|
||||
onChange={(e) => setNewStore({ ...newStore, lead: e.target.value })}
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#581c87]"
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#662582]"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -726,7 +761,7 @@ export default function App() {
|
||||
placeholder="₹1,50,000"
|
||||
value={newStore.sales}
|
||||
onChange={(e) => setNewStore({ ...newStore, sales: e.target.value })}
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#581c87]"
|
||||
className="w-full border border-[#e2e8f0] rounded-lg p-sm bg-[#f8fafc] focus:bg-white outline-none focus:ring-1 focus:ring-[#662582]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -742,7 +777,7 @@ export default function App() {
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-4 py-2 bg-[#581c87] text-white rounded-lg font-bold hover:bg-purple-800 cursor-pointer shadow-sm"
|
||||
className="px-4 py-2 bg-[#662582] text-white rounded-lg font-bold hover:bg-purple-800 cursor-pointer shadow-sm"
|
||||
>
|
||||
Create Outlet Node
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user