feat: add frontend dashboard for run results viewer
- Implemented main dashboard with run metrics and filtering options. - Created detailed view for individual runs with results and anomalies. - Added product listing page with filtering and pagination. - Introduced utility functions for formatting dates and numbers. - Styled components with CSS for a clean and responsive layout. - Set up HTML entry point and linked to the main JavaScript file. - Updated TypeScript configuration to include DOM types.
This commit is contained in:
59
src/llm.ts
59
src/llm.ts
@@ -7,9 +7,14 @@ Given product data, evaluate each product's viability for selling on Amazon. Con
|
||||
|
||||
1. **Sales Velocity**: monthlySold and salesRankDrops30 are the most important signals. A product that doesn't sell is worthless regardless of margin. salesRankDrops30 = approximate units sold in 30 days. monthlySold is Keepa's estimate.
|
||||
2. **Margin Analysis**: Sale price minus unit cost minus fees (FBA or FBM). Aim for >30% ROI minimum. The spreadsheet may include FBA NET and gross profit estimates — cross-check against Keepa pricing data.
|
||||
- If unitCost is 0, it means NO COST DATA is available — this is NOT a free product. Do not assume infinite or zero-cost margins. When estimatedROI is null and unitCost is 0, evaluate purely on demand and velocity signals; never extrapolate profitability.
|
||||
- If estimatedProfit is null, price data was unavailable at analysis time — treat as elevated uncertainty and be conservative.
|
||||
3. **Sales Rank (BSR)**: Lower rank = higher demand. Rank <50,000 is good, <1,000 is excellent.
|
||||
4. **Sales Rank Trend**: Compare current rank vs 90d average. Lower current = improving demand.
|
||||
5. **Competition**: Number of sellers and Buy Box dynamics. Fewer sellers = easier entry.
|
||||
5. **Competition & Buy Box**:
|
||||
- Fewer sellers = easier entry, but verify who holds the Buy Box.
|
||||
- If buyBoxSeller is "ATVPDKIKX0DER" (Amazon retail), the product is Amazon-exclusive — return "SKIP". Amazon-sold items cannot be competed with by third-party sellers.
|
||||
- If sellerCount is 1 and buyBoxSeller is not null, assume the market is closed — return "SKIP".
|
||||
6. **Price Stability**: Large price swings (high max vs low min over 90d) = volatile/risky.
|
||||
7. **FBA vs FBM**: FBA preferred for fast-selling, small/light items. FBM for oversized, slow-moving, or high-margin items where fee savings matter.
|
||||
8. **MOQ & Capital**: High MOQ with thin margins is risky.
|
||||
@@ -21,6 +26,7 @@ Given product data, evaluate each product's viability for selling on Amazon. Con
|
||||
|
||||
Decision policy:
|
||||
- Do not recommend products that cannot be listed by this seller account.
|
||||
- Do not recommend Amazon-exclusive products (buyBoxSeller = "ATVPDKIKX0DER").
|
||||
- Prioritize profitable + high-velocity + listable products.
|
||||
- Use "SKIP" when data quality is poor or risk is high.
|
||||
|
||||
@@ -106,12 +112,20 @@ function summarizeForLlm(p: EnrichedProduct) {
|
||||
const salePrice =
|
||||
p.keepa?.currentPrice ??
|
||||
p.record.sellingPriceFromSheet ??
|
||||
p.spApi.estimatedSalePrice;
|
||||
const referralFee = salePrice * (p.spApi.referralFeePercent / 100);
|
||||
(p.spApi.estimatedSalePrice > 0 ? p.spApi.estimatedSalePrice : null) ??
|
||||
p.keepa?.avgPrice90 ??
|
||||
null;
|
||||
|
||||
const referralFee =
|
||||
salePrice != null ? salePrice * (p.spApi.referralFeePercent / 100) : null;
|
||||
const fbaProfit =
|
||||
salePrice - p.record.unitCost - p.spApi.fbaFee - referralFee;
|
||||
salePrice != null && referralFee != null
|
||||
? salePrice - p.record.unitCost - p.spApi.fbaFee - referralFee
|
||||
: null;
|
||||
const fbmProfit =
|
||||
salePrice - p.record.unitCost - p.spApi.fbmFee - referralFee;
|
||||
salePrice != null && referralFee != null
|
||||
? salePrice - p.record.unitCost - p.spApi.fbmFee - referralFee
|
||||
: null;
|
||||
|
||||
return {
|
||||
asin: p.record.asin,
|
||||
@@ -121,7 +135,7 @@ function summarizeForLlm(p: EnrichedProduct) {
|
||||
p.record.category ?? p.keepa?.categoryTree?.join(" > "),
|
||||
60,
|
||||
),
|
||||
unitCost: p.record.unitCost,
|
||||
unitCost: p.record.unitCost > 0 ? p.record.unitCost : null,
|
||||
currentPrice: salePrice,
|
||||
priceRange90d: p.keepa
|
||||
? {
|
||||
@@ -133,6 +147,8 @@ function summarizeForLlm(p: EnrichedProduct) {
|
||||
salesRank: p.keepa?.salesRank ?? p.record.amazonRank,
|
||||
salesRankAvg90d: p.keepa?.salesRankAvg90,
|
||||
sellerCount: p.keepa?.sellerCount,
|
||||
buyBoxSeller: p.keepa?.buyBoxSeller ?? null,
|
||||
buyBoxPrice: p.keepa?.buyBoxPrice ?? null,
|
||||
salesVelocity: {
|
||||
monthlySold: p.keepa?.monthlySold,
|
||||
salesRankDrops30: p.keepa?.salesRankDrops30,
|
||||
@@ -155,27 +171,28 @@ function summarizeForLlm(p: EnrichedProduct) {
|
||||
fbaFee: p.spApi.fbaFee,
|
||||
fbmFee: p.spApi.fbmFee,
|
||||
referralFeePercent: p.spApi.referralFeePercent,
|
||||
referralFee: Math.round(referralFee * 100) / 100,
|
||||
referralFee:
|
||||
referralFee != null ? Math.round(referralFee * 100) / 100 : null,
|
||||
},
|
||||
sellerEligibility: {
|
||||
canSell: p.spApi.canSell,
|
||||
status: p.spApi.sellabilityStatus,
|
||||
reason: clampText(p.spApi.sellabilityReason, 120),
|
||||
},
|
||||
estimatedProfit: {
|
||||
fba: Math.round(fbaProfit * 100) / 100,
|
||||
fbm: Math.round(fbmProfit * 100) / 100,
|
||||
},
|
||||
estimatedROI: {
|
||||
fba:
|
||||
p.record.unitCost > 0
|
||||
? Math.round((fbaProfit / p.record.unitCost) * 100)
|
||||
: null,
|
||||
fbm:
|
||||
p.record.unitCost > 0
|
||||
? Math.round((fbmProfit / p.record.unitCost) * 100)
|
||||
: null,
|
||||
},
|
||||
estimatedProfit:
|
||||
fbaProfit != null && fbmProfit != null
|
||||
? {
|
||||
fba: Math.round(fbaProfit * 100) / 100,
|
||||
fbm: Math.round(fbmProfit * 100) / 100,
|
||||
}
|
||||
: null,
|
||||
estimatedROI:
|
||||
p.record.unitCost > 0 && fbaProfit != null && fbmProfit != null
|
||||
? {
|
||||
fba: Math.round((fbaProfit / p.record.unitCost) * 100),
|
||||
fbm: Math.round((fbmProfit / p.record.unitCost) * 100),
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user