← back to Govarbitrage
apps/mobile/lib/types.ts
245 lines
// TypeScript types derived directly from the GovArbitrage API response shapes.
// Source: src/lib/listings.ts (ListingRow), src/prisma/schema.prisma,
// src/app/api/listings/route.ts, src/lib/listing-detail.ts
// ── Enum mirrors ─────────────────────────────────────────────────────────────
export type AuctionSource =
| "GOVDEALS"
| "PUBLIC_SURPLUS"
| "GSA_AUCTIONS"
| "COUNTY"
| "STATE_SURPLUS"
| "UNIVERSITY_SURPLUS"
| "MUNICIBID"
| "BID4ASSETS"
| "GOINDUSTRY"
| "NETWORK_INTL"
| "GRAYS_AU"
| "GOVPLANET"
| "CSV"
| "EXTENSION"
| "OTHER";
export type Condition = "NEW" | "LIKE_NEW" | "USED_GOOD" | "USED_FAIR" | "FOR_PARTS" | "UNKNOWN";
export type RiskLevel = "LOW" | "MEDIUM" | "HIGH";
export type DropShipFeasibility = "EASY" | "MODERATE" | "DIFFICULT" | "INFEASIBLE";
export type ResearchStatus = "PENDING" | "QUEUED" | "IN_PROGRESS" | "COMPLETE" | "FAILED";
export type ListingStatus = "ACTIVE" | "ENDED" | "REMOVED";
export type Tier = "FREE" | "STANDARD" | "PREMIUM";
export type ScoreProfile =
| "OVERALL_OPPORTUNITY"
| "BEST_ARBITRAGE"
| "QUICK_FLIP"
| "COLLECTOR"
| "LOCAL_PICKUP"
| "EASY_FREIGHT"
| "PARTS_ONLY"
| "HIGH_CONFIDENCE"
| "HIGH_PROFIT";
// ── Flat listing row (from GET /api/listings) ────────────────────────────────
export interface ListingRow {
id: string;
source: AuctionSource;
sourceAuctionId: string;
sourceUrl: string | null;
title: string;
category: string | null;
manufacturer: string | null;
model: string | null;
condition: Condition;
quantity: number;
currentBid: number;
currentCost: number; // bid + premium + tax
/** Null for FREE tier — money-math gate */
recommendedMaxBid: number | null;
retailLow: number | null;
retailAverage: number | null;
retailHigh: number | null;
usedLow: number | null;
usedAverage: number | null;
usedHigh: number | null;
wholesale: number | null;
liquidation: number | null;
sellNow: number | null;
value7Day: number | null;
value30Day: number | null;
value90Day: number | null;
expectedSale: number | null;
shipping: number;
freight: number;
repairs: number;
marketplaceFees: number;
netProfit: number | null;
roi: number | null; // decimal ratio e.g. 0.42 = 42%
risk: RiskLevel;
confidence: number | null; // 0..100
opportunityScore: number | null;
arbitrageScore: number | null;
demandScore: number | null;
velocityScore: number | null;
logisticsScore: number | null;
conditionScore: number | null;
competitionScore: number | null;
buyerScore: number | null;
dropShip: DropShipFeasibility;
closingAt: string | null; // ISO8601
researchStatus: ResearchStatus;
imageUrl: string | null;
locationCity: string | null;
locationState: string | null;
// Now returned on list rows too (backend flattenListing), so the "Newest"
// sort and the admin created-date chip work on the grid, not just detail.
createdAt: string;
}
// ── Paginated list response ───────────────────────────────────────────────────
export interface ListingsResponse {
rows: ListingRow[];
total: number;
page: number;
pageSize: number;
tier: Tier;
gated: boolean;
}
// ── Detail response (GET /api/listings/:id) ───────────────────────────────────
export interface Research {
id: string;
newRetail: number | null;
newReplacement: number | null;
avgRetail: number | null;
usedSoldPrice: number | null;
usedAskingPrice: number | null;
usedLow: number | null;
usedHigh: number | null;
wholesaleValue: number | null;
liquidationValue: number | null;
sellTodayValue: number | null;
value7Day: number | null;
value30Day: number | null;
value90Day: number | null;
expectedSalePrice: number | null;
probabilityOfSale: number | null; // 0..1
daysUntilSold: number | null;
confidenceScore: number | null; // 0..100
summary: string | null;
createdAt: string;
}
export interface CostBreakdown {
id: string;
winningBid: number;
buyerPremium: number;
salesTax: number;
shipping: number;
freight: number;
insurance: number;
packing: number;
pickupLabor: number;
testing: number;
repairs: number;
certification: number;
marketplaceFees: number;
paymentFees: number;
storage: number;
photography: number;
listingLabor: number;
expectedReturns: number;
totalInvestment: number;
expectedNetProfit: number;
roi: number; // decimal ratio
annualizedReturn: number;
recommendedMaxBid: number;
}
export interface Score {
id: string;
profile: ScoreProfile;
value: number; // 0..100
arbitrage: number;
demand: number;
velocity: number;
logistics: number;
condition: number;
competition: number;
buyer: number;
risk: RiskLevel;
dropShip: DropShipFeasibility;
explanation: string;
}
export interface Comparable {
id: string;
kind: "SOLD" | "ACTIVE" | "RETAIL";
title: string;
price: number;
url: string | null;
source: string | null;
soldAt: string | null;
}
export interface ListingDetail {
id: string;
source: AuctionSource;
sourceAuctionId: string;
sourceUrl: string | null;
title: string;
description: string | null;
category: string | null;
manufacturer: string | null;
model: string | null;
serialNumber: string | null;
condition: Condition;
quantity: number;
accessories: string | null;
missingParts: string | null;
weightLbs: number | null;
dimensions: string | null;
locationCity: string | null;
locationState: string | null;
locationZip: string | null;
currentBid: number;
bidCount: number;
closingAt: string | null;
imageUrls: string[];
researchStatus: ResearchStatus;
listingStatus: ListingStatus;
createdAt: string;
updatedAt: string;
research: Research | null;
costBreakdown: CostBreakdown | null;
scores: Score[];
comparables: Comparable[];
tier: Tier;
gated: boolean;
}
// ── Query params ──────────────────────────────────────────────────────────────
export type SortField =
| "opportunityScore"
| "roi"
| "netProfit"
| "closingAt"
| "currentBid"
| "createdAt";
export interface ListingsQueryParams {
search?: string;
source?: AuctionSource;
category?: string;
condition?: Condition;
risk?: RiskLevel;
closingWithinHours?: number;
sort?: SortField;
dir?: "asc" | "desc";
page?: number;
pageSize?: number;
profile?: ScoreProfile;
}