- Updated `SupplierAnalysisResult` to include a `product` field and modified related tests. - Refactored `addRowsSheet` to accommodate changes in the product structure. - Enhanced UPC file analysis to utilize a new `toSupplierInputRecord` function for cleaner record creation. - Introduced new types for supplier input records and product observations. - Updated frontend components to handle new product details and analysis history. - Improved database writing functions to streamline run completion and error handling. - Added new API endpoints for product details and adjusted routing in the frontend.
220 lines
4.7 KiB
TypeScript
220 lines
4.7 KiB
TypeScript
export interface ProductRecord {
|
|
asin: string;
|
|
name: string;
|
|
unitCost: number;
|
|
brand?: string;
|
|
category?: string;
|
|
amazonRank?: number;
|
|
avgPrice90FromSheet?: number;
|
|
sellingPriceFromSheet?: number;
|
|
fbaNet?: number;
|
|
grossProfit?: number;
|
|
grossProfitPct?: number;
|
|
netProfitFromSheet?: number;
|
|
roiFromSheet?: number;
|
|
moq?: number;
|
|
moqCost?: number;
|
|
totalQtyAvail?: number;
|
|
|
|
link?: string;
|
|
asinLink?: string;
|
|
sourceUrl?: string;
|
|
supplier?: string;
|
|
promoCouponCode?: string;
|
|
notes?: string;
|
|
leadDate?: string;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export interface KeepaData {
|
|
currentPrice: number | null;
|
|
avgPrice90: number | null;
|
|
minPrice90: number | null;
|
|
maxPrice90: number | null;
|
|
salesRank: number | null;
|
|
salesRankAvg90: number | null;
|
|
salesRankDrops30: number | null;
|
|
salesRankDrops90: number | null;
|
|
sellerCount: number | null;
|
|
amazonIsSeller: boolean | null;
|
|
amazonBuyboxSharePct90d: number | null;
|
|
buyBoxSeller: string | null;
|
|
buyBoxPrice: number | null;
|
|
buyBoxAvg90?: number | null;
|
|
monthlySold: number | null;
|
|
categoryTree: string[];
|
|
}
|
|
|
|
export type KeepaUpcLookupStatus =
|
|
| "found"
|
|
| "invalid_upc"
|
|
| "not_found"
|
|
| "multiple_asins"
|
|
| "request_failed";
|
|
|
|
export interface KeepaUpcLookupDetail {
|
|
requestedUpc: string;
|
|
normalizedUpc: string;
|
|
status: KeepaUpcLookupStatus;
|
|
asin: string | null;
|
|
candidateAsins: string[];
|
|
keepaData: KeepaData | null;
|
|
provider?: "sp_api" | "keepa";
|
|
reason?: string;
|
|
}
|
|
|
|
export type UpcLookupDetail = KeepaUpcLookupDetail;
|
|
|
|
export type SellabilityInfo = {
|
|
canSell: boolean | null;
|
|
sellabilityStatus: "available" | "restricted" | "not_available" | "unknown";
|
|
sellabilityReason?: string;
|
|
};
|
|
|
|
export interface SpApiData extends SellabilityInfo {
|
|
fbaFee: number;
|
|
fbmFee: number;
|
|
referralFeePercent: number;
|
|
estimatedSalePrice: number;
|
|
}
|
|
|
|
export interface EnrichedProduct {
|
|
record: ProductRecord;
|
|
keepa: KeepaData | null;
|
|
spApi: SpApiData;
|
|
fetchedAt: string;
|
|
}
|
|
|
|
export interface LlmVerdict {
|
|
asin: string;
|
|
verdict: "FBA" | "FBM" | "SKIP";
|
|
confidence: number;
|
|
reasoning: string;
|
|
}
|
|
|
|
export interface AnalysisResult {
|
|
product: EnrichedProduct;
|
|
verdict: LlmVerdict;
|
|
}
|
|
|
|
export type SupplierVerdict = "BUY" | "WATCH" | "SKIP";
|
|
|
|
export interface SupplierScore {
|
|
salePrice: number | null;
|
|
fbaFee: number | null;
|
|
profit: number | null;
|
|
margin: number | null;
|
|
roi: number | null;
|
|
demandScore: number;
|
|
competitionPenalty: number;
|
|
score: number;
|
|
verdict: SupplierVerdict;
|
|
reason: string;
|
|
}
|
|
|
|
export interface SupplierAnalysisResult {
|
|
upc: string;
|
|
rowNumber?: number;
|
|
record: SupplierInputRecord;
|
|
product: ProductRecord | null;
|
|
lookup: UpcLookupDetail;
|
|
keepa: KeepaData | null;
|
|
spApi: SpApiData | null;
|
|
score: SupplierScore;
|
|
fetchedAt: string;
|
|
}
|
|
|
|
export interface SupplierInputRecord {
|
|
name: string;
|
|
unitCost: number;
|
|
brand?: string;
|
|
category?: string;
|
|
}
|
|
|
|
export interface Product {
|
|
asin: string;
|
|
name: string | null;
|
|
brand: string | null;
|
|
category: string | null;
|
|
firstSeenAt: string;
|
|
lastSeenAt: string;
|
|
}
|
|
|
|
export interface ProductObservation {
|
|
id: number;
|
|
productAsin: string;
|
|
runId: number;
|
|
source: string;
|
|
fetchedAt: string;
|
|
}
|
|
|
|
export interface Run {
|
|
id: number;
|
|
type:
|
|
| "lead_analysis"
|
|
| "category_analysis"
|
|
| "supplier_upc"
|
|
| "stalker"
|
|
| "stalker_analysis";
|
|
parentRunId?: number | null;
|
|
status: string;
|
|
}
|
|
|
|
export interface RunItem {
|
|
id: number;
|
|
runId: number;
|
|
productAsin: string | null;
|
|
sourceRow?: number | null;
|
|
}
|
|
|
|
export interface AnalysisRevision {
|
|
id: number;
|
|
runItemId: number;
|
|
decision: "FBA" | "FBM" | "BUY" | "WATCH" | "SKIP";
|
|
confidence: number | null;
|
|
reasoning: string | null;
|
|
analyzedAt: string;
|
|
}
|
|
|
|
export interface CategoryRunSummaryDb {
|
|
categoryId: number;
|
|
categoryLabel: string;
|
|
runTimestamp: string;
|
|
topAsinsChecked: number;
|
|
availableAsins: number;
|
|
fbaCount: number;
|
|
fbmCount: number;
|
|
skipCount: number;
|
|
status: "ok" | "empty" | "failed";
|
|
errorMessage?: string;
|
|
}
|
|
|
|
export interface ProductAnalysisResultDb {
|
|
asin: string;
|
|
runId: number;
|
|
name: string;
|
|
brand?: string;
|
|
category?: string;
|
|
unitCost?: number;
|
|
currentPrice?: number;
|
|
avgPrice90d?: number;
|
|
avgPrice90dSheet?: number;
|
|
sellingPriceSheet?: number;
|
|
salesRank?: number;
|
|
salesRankAvg90d?: number;
|
|
sellerCount?: number;
|
|
monthlySold?: number;
|
|
rankDrops30d?: number;
|
|
rankDrops90d?: number;
|
|
fbaFee?: number;
|
|
fbmFee?: number;
|
|
referralPercent?: number;
|
|
canSell?: string;
|
|
sellabilityStatus?: string;
|
|
sellabilityReason?: string;
|
|
verdict: string;
|
|
confidence: number;
|
|
reasoning?: string;
|
|
fetchedAt: string;
|
|
}
|