new changes in api base url
This commit is contained in:
@@ -58,6 +58,7 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
const [view, setView] = useState<'catalogue' | 'inventory' | 'requests'>('catalogue');
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
||||
const [requestDate, setRequestDate] = useState(() => new Date().toISOString().split('T')[0]);
|
||||
const [stockHealthFilter, setStockHealthFilter] = useState<string[]>([]);
|
||||
const [selectedProduct, setSelectedProduct] = useState<any>(null);
|
||||
const [hoveredProduct, setHoveredProduct] = useState<any>(null);
|
||||
@@ -82,7 +83,7 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
[storeCat.items],
|
||||
);
|
||||
|
||||
const stockRequestsQ = useFiestaGetStockRequests({ tenantid, locationid: locationid ?? 0, pagesize: 500 });
|
||||
const stockRequestsQ = useFiestaGetStockRequests({ tenantid, locationid: locationid ?? 0, pagesize: 500, date: requestDate });
|
||||
const [picks, setPicks] = useState<Record<string, { qty: number; status: 'Pending' | 'Approved' | 'Rejected' | 'Cancelled' | 'Received'; requestedAt: string; resolvedAt?: string }>>({});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -108,43 +109,45 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
|
||||
const togglePick = (id: string) => {
|
||||
setNotice(false);
|
||||
setPicks((prev) => {
|
||||
const next = { ...prev };
|
||||
if (next[id] != null && next[id].status !== 'Cancelled') {
|
||||
next[id] = { ...next[id], status: 'Cancelled', resolvedAt: new Date().toISOString() };
|
||||
} else {
|
||||
next[id] = { qty: 1, status: 'Pending', requestedAt: new Date().toISOString() };
|
||||
createRequestMutation.mutate({
|
||||
tenantid,
|
||||
locationid: locationid ?? FIESTA_PRIMARY_LOCATION_ID,
|
||||
productid: Number(id),
|
||||
qty: 1,
|
||||
status: 'Pending'
|
||||
});
|
||||
}
|
||||
return next;
|
||||
});
|
||||
const existing = picks[id];
|
||||
if (existing != null && existing.status !== 'Cancelled') {
|
||||
setPicks(prev => ({ ...prev, [id]: { ...prev[id], status: 'Cancelled', resolvedAt: new Date().toISOString() } }));
|
||||
} else {
|
||||
setPicks(prev => ({ ...prev, [id]: { qty: 1, status: 'Pending', requestedAt: new Date().toISOString() } }));
|
||||
createRequestMutation.mutate({
|
||||
tenantid,
|
||||
locationid: locationid ?? FIESTA_PRIMARY_LOCATION_ID,
|
||||
productid: Number(id),
|
||||
qty: 1,
|
||||
status: 'Pending',
|
||||
locationname: storeName
|
||||
});
|
||||
}
|
||||
};
|
||||
const setPickQty = (id: string, qty: number) => setPicks((prev) => {
|
||||
const existing = prev[id] || {};
|
||||
|
||||
const setPickQty = (id: string, qty: number) => {
|
||||
const safeQty = Math.max(1, Math.round(qty) || 1);
|
||||
setPicks((prev) => {
|
||||
const existing = prev[id] || {};
|
||||
return {
|
||||
...prev,
|
||||
[id]: {
|
||||
...existing,
|
||||
qty: safeQty,
|
||||
status: 'Pending',
|
||||
requestedAt: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
});
|
||||
createRequestMutation.mutate({
|
||||
tenantid,
|
||||
locationid: locationid ?? FIESTA_PRIMARY_LOCATION_ID,
|
||||
productid: Number(id),
|
||||
qty: safeQty,
|
||||
status: 'Pending'
|
||||
status: 'Pending',
|
||||
locationname: storeName
|
||||
});
|
||||
return {
|
||||
...prev,
|
||||
[id]: {
|
||||
...existing,
|
||||
qty: safeQty,
|
||||
status: 'Pending',
|
||||
requestedAt: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
const pickCount = Object.keys(picks).length;
|
||||
|
||||
// Store inventory (live stock) for the "My Store Inventory" tab + "In Store" tags.
|
||||
@@ -512,7 +515,21 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
|
||||
{/* ── My Requests ── */}
|
||||
{view === 'requests' && (
|
||||
Object.keys(picks).length === 0 ? (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-end bg-white px-4 py-3 rounded-2xl border border-slate-200 shadow-sm">
|
||||
<div className="flex items-center gap-3">
|
||||
<label htmlFor="requestDate" className="text-sm font-bold text-slate-500">Date:</label>
|
||||
<input
|
||||
type="date"
|
||||
id="requestDate"
|
||||
value={requestDate}
|
||||
onChange={(e) => setRequestDate(e.target.value)}
|
||||
className="px-3 py-1.5 border border-slate-200 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm font-medium text-slate-700 bg-slate-50"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(!stockRequestsQ.data || stockRequestsQ.data.length === 0) ? (
|
||||
<CenterState
|
||||
icon={<Inbox size={34} />}
|
||||
title="No stock requested yet"
|
||||
@@ -524,45 +541,48 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
<table className="w-full text-left border-collapse" style={{ minWidth: 800 }}>
|
||||
<thead>
|
||||
<tr className="bg-slate-50/80 border-b border-slate-200">
|
||||
{['Requested At', 'Product', 'Qty', 'Status', 'Resolved At', ''].map((h, i) => (
|
||||
{['Requested At', 'Store', 'Product', 'Qty', 'Status', 'Resolved At', ''].map((h, i) => (
|
||||
<th key={i} className="px-5 py-4 text-[10px] font-black uppercase tracking-widest text-slate-500 whitespace-nowrap">{h}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{Object.entries(picks).map(([pid, data]: [string, any]) => {
|
||||
const prod = products.find(p => p.id === pid);
|
||||
{(stockRequestsQ.data || []).map((data: any) => {
|
||||
const pid = String(data.productid);
|
||||
const prod = products.find(p => String(p.id) === pid);
|
||||
const isApproved = data.status === 'Approved';
|
||||
const isRejected = data.status === 'Rejected';
|
||||
const isCancelled = data.status === 'Cancelled';
|
||||
|
||||
return (
|
||||
<tr key={pid} className="transition-all duration-200 hover:bg-slate-50/80 group">
|
||||
<tr key={data.requestid} className="transition-all duration-200 hover:bg-slate-50/80 group">
|
||||
<td className="px-5 py-4 whitespace-nowrap">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-semibold text-slate-900">
|
||||
{data.requestedAt ? new Date(data.requestedAt).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' }) : '—'}
|
||||
{data.created ? new Date(data.created).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' }) : '—'}
|
||||
</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' }) : ''}
|
||||
{data.created ? new Date(data.created).toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit' }) : ''}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-5 py-4 whitespace-nowrap">
|
||||
<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">
|
||||
<Store size={14} className="text-indigo-500" />
|
||||
<span className="font-bold text-xs">{storeName}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-5 py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
{prod ? (
|
||||
<>
|
||||
<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">
|
||||
<img src={prod.image} alt={prod.name} className="w-full h-full object-cover" />
|
||||
<img src={data.productimage || prod?.image || PLACEHOLDER} alt={data.productname || prod?.name || 'Product'} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<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>
|
||||
<p className="font-bold text-sm text-slate-900 truncate group-hover:text-indigo-700 transition-colors">{data.productname || prod?.name}</p>
|
||||
<p className="text-xs font-medium text-slate-500 truncate mt-0.5">{prod?.sku || `SKU-${data.productid}`}</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<span className="text-xs text-slate-400">Product Not Found ({pid})</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-5 py-4 whitespace-nowrap">
|
||||
@@ -588,7 +608,7 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
</td>
|
||||
<td className="px-5 py-4 whitespace-nowrap">
|
||||
<span className="text-xs font-medium text-slate-500">
|
||||
{data.resolvedAt ? new Date(data.resolvedAt).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : '—'}
|
||||
{data.updated ? new Date(data.updated).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' }) : '—'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-5 py-4 whitespace-nowrap text-right">
|
||||
@@ -599,7 +619,7 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
updateRequestMutation.mutate({
|
||||
tenantid,
|
||||
locationid: locationid ?? FIESTA_PRIMARY_LOCATION_ID,
|
||||
productid: Number(pid),
|
||||
productid: Number(data.productid),
|
||||
requestid: data.requestid,
|
||||
status: 'Received'
|
||||
});
|
||||
@@ -612,12 +632,13 @@ export default function StoreCatalogView({ locationid, storeName = 'your store',
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── My Store Inventory ── */}
|
||||
|
||||
Reference in New Issue
Block a user