feat: enhance stalker functionality with inventory sellability checks and update frontend display

This commit is contained in:
Victor Noguera
2026-05-19 18:35:55 -04:00
parent a7c0e44e3d
commit aed0c11017
6 changed files with 317 additions and 78 deletions

View File

@@ -114,22 +114,15 @@ type StalkerResultItem = {
started_at: string;
status: string;
input_file: string;
source_asin: string;
title: string | null;
offer_count: number;
candidate_seller_count: number;
matched_seller_count: number;
scan_fetched_at: string;
seller_id: string;
seller_name: string | null;
rating: number | null;
rating_count: number | null;
storefront_asin_total: number | null;
persisted_inventory_sample_count: number | null;
offer_price: number | null;
condition: string | null;
is_fba: number | null;
stock: number | null;
discovered_from_count: number;
first_seen_at: string;
last_seen_at: string;
persisted_inventory_asin_count: number;
inventory_sample_asins: string | null;
};
@@ -138,7 +131,6 @@ type StalkerResultsResponse = {
items: StalkerResultItem[];
summary: {
runs: number;
sourceAsins: number;
sellers: number;
persistedInventoryAsins: number;
};
@@ -907,7 +899,7 @@ function StalkerExplorer({ onBack }: { onBack: () => void }) {
const [maxRatingCount, setMaxRatingCount] = useState("30");
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(25);
const [sort, setSort] = useState<SortState>({ field: "started_at", direction: "DESC" });
const [sort, setSort] = useState<SortState>({ field: "persisted_inventory_asin_count", direction: "DESC" });
useEffect(() => {
let cancelled = false;
@@ -943,7 +935,7 @@ function StalkerExplorer({ onBack }: { onBack: () => void }) {
<button className="back" onClick={onBack}>Back</button>
<div className="card">
<h2>Stalker Results</h2>
<h2>Seller Storefronts</h2>
</div>
<div className="metrics">
@@ -951,23 +943,19 @@ function StalkerExplorer({ onBack }: { onBack: () => void }) {
<div className="label">Runs</div>
<div className="value">{formatNumber(results?.summary.runs)}</div>
</div>
<div className="metric">
<div className="label">Source ASINs</div>
<div className="value">{formatNumber(results?.summary.sourceAsins)}</div>
</div>
<div className="metric">
<div className="label">Matched sellers</div>
<div className="value">{formatNumber(results?.summary.sellers)}</div>
</div>
<div className="metric">
<div className="label">Persisted inventory ASINs</div>
<div className="label">Sellable inventory ASINs</div>
<div className="value">{formatNumber(results?.summary.persistedInventoryAsins)}</div>
</div>
</div>
<div className="card">
<div className="toolbar">
<input value={search} onChange={(e) => { setPage(1); setSearch(e.target.value); }} placeholder="Search source ASIN/title/seller/inventory" />
<input value={search} onChange={(e) => { setPage(1); setSearch(e.target.value); }} placeholder="Search seller or sellable ASIN" />
<input value={sellerId} onChange={(e) => { setPage(1); setSellerId(e.target.value.toUpperCase()); }} placeholder="Seller ID" />
<input value={runId} onChange={(e) => { setPage(1); setRunId(e.target.value); }} placeholder="Run ID" />
<input value={minRatingCount} onChange={(e) => { setPage(1); setMinRatingCount(e.target.value); }} placeholder="Min rating count" />
@@ -986,24 +974,20 @@ function StalkerExplorer({ onBack }: { onBack: () => void }) {
<thead>
<tr>
<th><button onClick={() => setSort(nextSort(sort, "runId"))}>Run</button></th>
<th><button onClick={() => setSort(nextSort(sort, "started_at"))}>Started</button></th>
<th><button onClick={() => setSort(nextSort(sort, "source_asin"))}>Source ASIN</button></th>
<th><button onClick={() => setSort(nextSort(sort, "title"))}>Title</button></th>
<th><button onClick={() => setSort(nextSort(sort, "seller_id"))}>Seller ID</button></th>
<th><button onClick={() => setSort(nextSort(sort, "seller_name"))}>Seller</button></th>
<th><button onClick={() => setSort(nextSort(sort, "rating"))}>Rating</button></th>
<th><button onClick={() => setSort(nextSort(sort, "rating_count"))}>Rating Count</button></th>
<th><button onClick={() => setSort(nextSort(sort, "offer_price"))}>Offer</button></th>
<th>FBA</th>
<th><button onClick={() => setSort(nextSort(sort, "stock"))}>Stock</button></th>
<th><button onClick={() => setSort(nextSort(sort, "discovered_from_count"))}>Hits</button></th>
<th><button onClick={() => setSort(nextSort(sort, "storefront_asin_total"))}>Storefront total</button></th>
<th><button onClick={() => setSort(nextSort(sort, "persisted_inventory_asin_count"))}>Persisted sample</button></th>
<th className="inventory-col">Inventory ASIN sample</th>
<th><button onClick={() => setSort(nextSort(sort, "persisted_inventory_asin_count"))}>Sellable sample</button></th>
<th><button onClick={() => setSort(nextSort(sort, "last_seen_at"))}>Last Seen</button></th>
<th className="inventory-col">Sellable inventory ASIN sample</th>
</tr>
</thead>
<tbody>
{loading ? (
<tr><td colSpan={14}>Loading...</td></tr>
<tr><td colSpan={10}>Loading...</td></tr>
) : results?.items.length ? (
results.items.map((item) => {
const inventorySample = (item.inventory_sample_asins ?? "")
@@ -1011,20 +995,16 @@ function StalkerExplorer({ onBack }: { onBack: () => void }) {
.filter(Boolean)
.slice(0, 20);
return (
<tr key={`${item.runId}-${item.source_asin}-${item.seller_id}`}>
<tr key={`${item.runId}-${item.seller_id}`}>
<td>{item.runId}</td>
<td>{formatDate(item.started_at)}</td>
<td><a href={`http://amazon.com/dp/${item.source_asin}`} target="_blank" rel="noreferrer">{item.source_asin}</a></td>
<td className="product-col">{item.title || "-"}</td>
<td>{item.seller_id}</td>
<td>{item.seller_name || "-"}</td>
<td>{formatNumber(item.rating)}</td>
<td>{formatNumber(item.rating_count)}</td>
<td>{formatCurrency(item.offer_price)}</td>
<td>{formatBoolean(item.is_fba)}</td>
<td>{formatNumber(item.stock)}</td>
<td>{formatNumber(item.discovered_from_count)}</td>
<td>{formatNumber(item.storefront_asin_total)}</td>
<td>{formatNumber(item.persisted_inventory_asin_count)}</td>
<td>{formatDate(item.last_seen_at)}</td>
<td className="inventory-col">
{inventorySample.length === 0 ? "-" : inventorySample.map((asin) => (
<a key={asin} href={`http://amazon.com/dp/${asin}`} target="_blank" rel="noreferrer">{asin}</a>
@@ -1034,7 +1014,7 @@ function StalkerExplorer({ onBack }: { onBack: () => void }) {
);
})
) : (
<tr><td colSpan={14}>No stalker results found</td></tr>
<tr><td colSpan={10}>No seller storefronts found</td></tr>
)}
</tbody>
</table>