From 92165b986c5b0777fb2bed4c00c3a065d5bf4d96 Mon Sep 17 00:00:00 2001 From: abhishek Date: Thu, 23 Jul 2026 16:05:38 +0530 Subject: [PATCH] inventory page --- src/App.tsx | 2 +- src/components/CatalogueBrowser.tsx | 9 +- src/components/ImportProductModal.tsx | 6 +- src/components/InventoryView.tsx | 58 ++++++++----- src/components/StoreCatalogView.tsx | 119 +++++++++++++++++++------- src/components/SuperAdminPage.tsx | 2 +- src/components/UserStorePage.tsx | 2 +- src/services/fiestaQueries.ts | 1 + src/services/storeCatalogue.ts | 22 ++--- 9 files changed, 150 insertions(+), 71 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ba65e8b..0e49eda 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -115,7 +115,7 @@ export default function App() { const [isCoimbatoreView, setIsCoimbatoreView] = useState(false); const [searchQuery, setSearchQuery] = useState(''); - const [sidebarOpen, setSidebarOpen] = useState(() => typeof window !== 'undefined' ? window.innerWidth >= 768 : true); + const [sidebarOpen, setSidebarOpen] = useState(false); // Auto adapt sidebar state on window resize useEffect(() => { diff --git a/src/components/CatalogueBrowser.tsx b/src/components/CatalogueBrowser.tsx index d317073..1da7c6b 100644 --- a/src/components/CatalogueBrowser.tsx +++ b/src/components/CatalogueBrowser.tsx @@ -24,7 +24,7 @@ export default function CatalogueBrowser({ tenantid, locationid, onClose }: Cata const [importingProduct, setImportingProduct] = useState(null); - const [isLocalSidebarOpen, setIsLocalSidebarOpen] = useState(true); + const [isLocalSidebarOpen, setIsLocalSidebarOpen] = useState(false); // Debounce search React.useEffect(() => { @@ -42,7 +42,7 @@ export default function CatalogueBrowser({ tenantid, locationid, onClose }: Cata const { data: importedRefs = new Set() } = useImportedCatalogueRefs(tenantid, brand); const { data: tenantCategories = [] } = useTenantCategories(tenantid); - const storeCat = useStoreCatalogue(); + const storeCat = useStoreCatalogue(tenantid, locationid); const importProductMutation = useImportCatalogueProduct(tenantid, locationid); @@ -238,7 +238,10 @@ export default function CatalogueBrowser({ tenantid, locationid, onClose }: Cata subcategoryid: 0, quantity: 1, stocktype: "in", - status: "Active", + // Draft: lands in the Admin Catalogue only. It only becomes + // visible to store users once the admin explicitly publishes + // it via "Add to Store Catalogue" (InventoryView). + status: "Draft", retailprice: 0, productcost: 0, taxpercent: 0, diff --git a/src/components/ImportProductModal.tsx b/src/components/ImportProductModal.tsx index 452c2b5..fcbc5a4 100644 --- a/src/components/ImportProductModal.tsx +++ b/src/components/ImportProductModal.tsx @@ -54,7 +54,9 @@ export default function ImportProductModal({ subcategoryid: subcategoryId ? Number(subcategoryId) : 0, quantity: Number(quantity), stocktype: "in", - status: "Active", + // Draft: lands in the Admin Catalogue only, published to the store + // catalogue separately via the explicit "Add to Store Catalogue" step. + status: "Draft", retailprice: Number(retailPrice), productcost: Number(productCost), taxpercent: Number(taxPercent), @@ -143,7 +145,7 @@ export default function ImportProductModal({ subcategoryid: 0, quantity: 1, stocktype: "in", - status: "Active", + status: "Draft", retailprice: 0, productcost: 0, taxpercent: 0, diff --git a/src/components/InventoryView.tsx b/src/components/InventoryView.tsx index 152956b..8d2f0b9 100644 --- a/src/components/InventoryView.tsx +++ b/src/components/InventoryView.tsx @@ -97,6 +97,11 @@ export default function InventoryView({ [locationsQ.data], ); + // The admin catalogue (imports, curation) is scoped to this tenant's own + // primary/hub outlet — not the hardcoded Fiesta defaults, so onboarded + // tenants other than Fiesta see their own imports reflected here. + const primaryLocationId = locations[0]?.locationid ?? FIESTA_PRIMARY_LOCATION_ID; + const storesStock = useFiestaStoresStock( tenantId, locations.map(({ locationid, locationname }) => ({ locationid, locationname })), @@ -113,11 +118,11 @@ export default function InventoryView({ const allStoreRows = storesStock.flatMap((s) => s.rows); const [activeTab, setActiveTab] = useState<'catalog' | 'requests' | 'global_catalogue'>('catalog'); - const [isLocalSidebarOpen, setIsLocalSidebarOpen] = useState(true); + const [isLocalSidebarOpen, setIsLocalSidebarOpen] = useState(false); const [selectedCategories, setSelectedCategories] = useState([]); const [hoveredAdminProduct, setHoveredAdminProduct] = useState(null); const [localSearch, setLocalSearch] = useState(''); - const storeCat = useStoreCatalogue(); + const storeCat = useStoreCatalogue(tenantId, primaryLocationId); const [importPrice, setImportPrice] = useState(''); const [isSettingPrice, setIsSettingPrice] = useState(false); const [addingPriceProdId, setAddingPriceProdId] = useState(null); @@ -613,7 +618,10 @@ export default function InventoryView({ disabled={!cardImportPrice || Number(cardImportPrice) <= 0} onClick={(e) => { e.stopPropagation(); - storeCat.add({ productid: prod.id, name: prod.name, image: prod.image, category: prod.category, sku: prod.sku, price: Number(cardImportPrice), unit: prod.exposure, qty: 1, status: 'Active' }); + // qty: 0 — publishing to the catalogue is a listing/pricing action, + // not a stock delivery. Real quantity only lands via the + // request → approve → Mark as Received flow. + storeCat.add({ productid: prod.id, name: prod.name, image: prod.image, category: prod.category, sku: prod.sku, price: Number(cardImportPrice), unit: prod.exposure, qty: 0, status: 'Active' }); setAddingPriceProdId(null); }} className="flex-1 py-1 bg-[#662582] text-white text-[9px] font-bold disabled:opacity-50" @@ -746,6 +754,7 @@ export default function InventoryView({ const isRejected = req.pickData.status === 'Rejected'; const isPending = req.pickData.status === 'Pending'; const isCancelled = req.pickData.status === 'Cancelled'; + const isReceived = req.pickData.status === 'Received'; return ( {req.pickData.status || '—'} @@ -843,7 +854,7 @@ export default function InventoryView({
setActiveTab('catalog')} />
@@ -940,19 +951,24 @@ export default function InventoryView({ - )} - + {picks[p.id].status !== 'Approved' && ( + + )} ) : ( @@ -532,6 +573,7 @@ export default function StoreCatalogView({ locationid, storeName = 'your store', const isApproved = data.status === 'Approved'; const isRejected = data.status === 'Rejected'; const isCancelled = data.status === 'Cancelled'; + const isReceived = data.status === 'Received'; return ( @@ -571,15 +613,17 @@ export default function StoreCatalogView({ locationid, storeName = 'your store', {data.status || '—'} @@ -595,15 +639,10 @@ export default function StoreCatalogView({ locationid, storeName = 'your store', @@ -828,6 +867,7 @@ export default function StoreCatalogView({ locationid, storeName = 'your store', const isPending = pick != null && pick.status === 'Pending'; const isApproved = pick != null && pick.status === 'Approved'; const isRejected = pick != null && pick.status === 'Rejected'; + const isReceived = pick != null && pick.status === 'Received'; const isRequested = pick != null && pick.status !== 'Cancelled'; return ( @@ -869,10 +909,27 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',

Stock Management

- {isApproved ? ( -
- - Approved and stocked in your inventory + {isReceived ? ( +
+ + Received and stocked in your inventory +
+ ) : isApproved ? ( +
+
+ + Approved ({pick.qty} units) — confirm once it arrives +
+
) : isRejected ? (
diff --git a/src/components/SuperAdminPage.tsx b/src/components/SuperAdminPage.tsx index 2ab7ee7..a428cc6 100644 --- a/src/components/SuperAdminPage.tsx +++ b/src/components/SuperAdminPage.tsx @@ -32,7 +32,7 @@ const NAV_ITEMS: SuperAdminNavItem[] = [ ]; export default function SuperAdminPage({ user, onLogout }: SuperAdminPageProps) { - const [sidebarOpen, setSidebarOpen] = useState(true); + const [sidebarOpen, setSidebarOpen] = useState(false); const location = useLocation(); const profile = { name: user.name, role: 'Super Admin', email: user.email }; const tenantId = user.tenantid || FIESTA_TENANT_ID; diff --git a/src/components/UserStorePage.tsx b/src/components/UserStorePage.tsx index adacc8d..8056a86 100644 --- a/src/components/UserStorePage.tsx +++ b/src/components/UserStorePage.tsx @@ -64,7 +64,7 @@ type StoreShape = React.ComponentProps['store']; * no other stores. Layout mirrors the admin console (fixed Header + left rail). */ export default function UserStorePage({ onLogout, user }: UserStorePageProps) { - const [sidebarOpen, setSidebarOpen] = useState(true); + const [sidebarOpen, setSidebarOpen] = useState(false); const [showQrModal, setShowQrModal] = useState(false); const location = useLocation(); const navigate = useNavigate(); diff --git a/src/services/fiestaQueries.ts b/src/services/fiestaQueries.ts index 4089cf8..a94067a 100644 --- a/src/services/fiestaQueries.ts +++ b/src/services/fiestaQueries.ts @@ -957,6 +957,7 @@ export function useFiestaCreateProductLocation() { qc.invalidateQueries({ queryKey: ['catalogue', 'imported'] }); qc.invalidateQueries({ queryKey: ['fiesta', 'productLocations'] }); qc.invalidateQueries({ queryKey: ['fiesta', 'productStocks'] }); + qc.invalidateQueries({ queryKey: ['fiesta', 'stockStatement'] }); }, onError: (error: any) => { alert(`Error adding product: ${error.message}`); diff --git a/src/services/storeCatalogue.ts b/src/services/storeCatalogue.ts index 83056e0..8c61945 100644 --- a/src/services/storeCatalogue.ts +++ b/src/services/storeCatalogue.ts @@ -37,10 +37,10 @@ export interface StoreCatalogueItem { * Live view of the store catalogue + curation helpers. Re-renders whenever the * catalogue changes via React Query invalidation. */ -export function useStoreCatalogue() { +export function useStoreCatalogue(tenantid: number = FIESTA_TENANT_ID, locationid: number = FIESTA_PRIMARY_LOCATION_ID) { const q = useFiestaProductLocations({ - tenantid: FIESTA_TENANT_ID, - locationid: FIESTA_PRIMARY_LOCATION_ID, + tenantid, + locationid, pagesize: 500, }); @@ -64,8 +64,8 @@ export function useStoreCatalogue() { const add = (item: StoreCatalogueItem) => { mutation.mutate({ - tenantid: FIESTA_TENANT_ID, - locationid: FIESTA_PRIMARY_LOCATION_ID, + tenantid, + locationid, productid: Number(item.productid), qty: item.qty, price: item.price, @@ -77,8 +77,8 @@ export function useStoreCatalogue() { const item = items.find(i => i.productid === id); if (!item) return; deleteMutation.mutate({ - tenantid: FIESTA_TENANT_ID, - locationid: FIESTA_PRIMARY_LOCATION_ID, + tenantid, + locationid, productid: Number(id) }); }; @@ -88,8 +88,8 @@ export function useStoreCatalogue() { if (!item) return; const safeQty = Math.max(1, Math.round(qty) || 1); mutation.mutate({ - tenantid: FIESTA_TENANT_ID, - locationid: FIESTA_PRIMARY_LOCATION_ID, + tenantid, + locationid, productid: Number(id), qty: safeQty, price: item.price, @@ -101,8 +101,8 @@ export function useStoreCatalogue() { const item = items.find(i => i.productid === id); if (!item) return; mutation.mutate({ - tenantid: FIESTA_TENANT_ID, - locationid: FIESTA_PRIMARY_LOCATION_ID, + tenantid, + locationid, productid: Number(id), qty: item.qty || 0, price: price,