inventory page
This commit is contained in:
@@ -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<string[]>([]);
|
||||
const [hoveredAdminProduct, setHoveredAdminProduct] = useState<ProductMatrixItem | null>(null);
|
||||
const [localSearch, setLocalSearch] = useState('');
|
||||
const storeCat = useStoreCatalogue();
|
||||
const storeCat = useStoreCatalogue(tenantId, primaryLocationId);
|
||||
const [importPrice, setImportPrice] = useState<string>('');
|
||||
const [isSettingPrice, setIsSettingPrice] = useState(false);
|
||||
const [addingPriceProdId, setAddingPriceProdId] = useState<string | null>(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 (
|
||||
<tr
|
||||
@@ -787,15 +796,17 @@ export default function InventoryView({
|
||||
</td>
|
||||
<td className="px-5 py-4 whitespace-nowrap">
|
||||
<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' :
|
||||
isReceived ? 'bg-indigo-50 text-indigo-700 border-indigo-200' :
|
||||
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' :
|
||||
isReceived ? 'bg-indigo-500' :
|
||||
isApproved ? 'bg-emerald-500' :
|
||||
isRejected ? 'bg-rose-500' :
|
||||
isCancelled ? 'bg-slate-400' :
|
||||
'bg-amber-500 animate-pulse'
|
||||
}`} />
|
||||
{req.pickData.status || '—'}
|
||||
@@ -843,7 +854,7 @@ export default function InventoryView({
|
||||
<div className="flex-1 flex flex-col min-h-0 overflow-hidden animate-in fade-in duration-300">
|
||||
<CatalogueBrowser
|
||||
tenantid={tenantId}
|
||||
locationid={FIESTA_PRIMARY_LOCATION_ID}
|
||||
locationid={primaryLocationId}
|
||||
onClose={() => setActiveTab('catalog')}
|
||||
/>
|
||||
</div>
|
||||
@@ -940,19 +951,24 @@ export default function InventoryView({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
<button
|
||||
disabled={!importPrice || Number(importPrice) <= 0}
|
||||
onClick={() => {
|
||||
storeCat.add({
|
||||
productid: selectedAdminProduct.id,
|
||||
name: selectedAdminProduct.name,
|
||||
image: selectedAdminProduct.image,
|
||||
category: selectedAdminProduct.category,
|
||||
sku: selectedAdminProduct.sku,
|
||||
price: Number(importPrice),
|
||||
unit: selectedAdminProduct.exposure || 'Pc',
|
||||
qty: 1,
|
||||
status: 'Active'
|
||||
// First-time publish gets qty 0 — it's a listing/pricing action, not a
|
||||
// stock delivery; real quantity only lands via Mark as Received. If this
|
||||
// product is already published, preserve its existing qty so a mere price
|
||||
// edit here doesn't zero out stock that's since been received.
|
||||
const existingQty = storeCat.items.find(i => i.productid === selectedAdminProduct.id)?.qty ?? 0;
|
||||
storeCat.add({
|
||||
productid: selectedAdminProduct.id,
|
||||
name: selectedAdminProduct.name,
|
||||
image: selectedAdminProduct.image,
|
||||
category: selectedAdminProduct.category,
|
||||
sku: selectedAdminProduct.sku,
|
||||
price: Number(importPrice),
|
||||
unit: selectedAdminProduct.exposure || 'Pc',
|
||||
qty: existingQty,
|
||||
status: 'Active'
|
||||
});
|
||||
}}
|
||||
className="w-full flex items-center justify-center gap-2 py-3 rounded-xl text-xs font-extrabold transition-all bg-[#662582] hover:bg-[#531e6a] text-white shadow-md disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
|
||||
|
||||
Reference in New Issue
Block a user