116 lines
2.9 KiB
TypeScript
116 lines
2.9 KiB
TypeScript
// app/gunbuilder/[categoryId]/[partId]/data.ts
|
|
|
|
// Simulated data functions - Replace these with API calls when ready
|
|
// Example: export async function getPricingHistory(partId: string) { ... }
|
|
|
|
const API_BASE_URL =
|
|
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
|
|
|
|
export interface PriceHistoryPoint {
|
|
date: string; // ISO date string
|
|
price: number;
|
|
}
|
|
|
|
export interface RetailerOffer {
|
|
id: string;
|
|
retailerName: string;
|
|
price: number;
|
|
originalPrice?: number;
|
|
inStock: boolean;
|
|
affiliateUrl: string;
|
|
lastUpdated: string; // ISO date string
|
|
}
|
|
|
|
/**
|
|
* Get pricing history for a part
|
|
* TODO: Replace with API call: GET /api/parts/{partId}/pricing-history
|
|
*
|
|
* For now, we keep this simulated.
|
|
*/
|
|
export function getPricingHistory(
|
|
partId: string,
|
|
basePrice: number,
|
|
): PriceHistoryPoint[] {
|
|
// Generate 30 days of price history with some variation
|
|
const days = 30;
|
|
const history: PriceHistoryPoint[] = [];
|
|
const today = new Date();
|
|
|
|
for (let i = days - 1; i >= 0; i--) {
|
|
const date = new Date(today);
|
|
date.setDate(date.getDate() - i);
|
|
|
|
// Simulate price fluctuations (±15% variation)
|
|
const variation = (Math.random() - 0.5) * 0.3; // -15% to +15%
|
|
const price = basePrice * (1 + variation);
|
|
|
|
history.push({
|
|
date: date.toISOString().split("T")[0],
|
|
price: Math.round(price * 100) / 100,
|
|
});
|
|
}
|
|
|
|
return history;
|
|
}
|
|
|
|
/**
|
|
* Get retailer offers for a part
|
|
* Real implementation using Ballistic backend:
|
|
* GET /api/products/{productId}/offers
|
|
*/
|
|
export async function getRetailerOffers(
|
|
productId: string,
|
|
): Promise<RetailerOffer[]> {
|
|
const res = await fetch(
|
|
`${API_BASE_URL}/api/products/${productId}/offers`,
|
|
{
|
|
// This will be called from the client detail page,
|
|
// so we *don't* use cache here.
|
|
cache: "no-store",
|
|
},
|
|
);
|
|
|
|
if (!res.ok) {
|
|
throw new Error(
|
|
`Failed to load retailer offers (${res.status}) for product ${productId}`,
|
|
);
|
|
}
|
|
|
|
// Expected backend payload (example):
|
|
// [
|
|
// {
|
|
// "id": "uuid-or-int",
|
|
// "merchantName": "Aero Precision",
|
|
// "price": 189.99,
|
|
// "originalPrice": 210.0,
|
|
// "inStock": true,
|
|
// "buyUrl": "https://classic.avantlink.com/click.php?...",
|
|
// "lastUpdated": "2025-11-30T15:22:00Z"
|
|
// },
|
|
// ...
|
|
// ]
|
|
const data: Array<{
|
|
id: string | number;
|
|
merchantName: string;
|
|
price: number;
|
|
originalPrice?: number | null;
|
|
inStock: boolean;
|
|
buyUrl: string;
|
|
lastUpdated: string;
|
|
}> = await res.json();
|
|
|
|
const offers: RetailerOffer[] = data
|
|
.map((o) => ({
|
|
id: String(o.id),
|
|
retailerName: o.merchantName,
|
|
price: o.price,
|
|
originalPrice: o.originalPrice ?? undefined,
|
|
inStock: o.inStock,
|
|
affiliateUrl: o.buyUrl,
|
|
lastUpdated: o.lastUpdated,
|
|
}))
|
|
// Sort by lowest price first
|
|
.sort((a, b) => a.price - b.price);
|
|
|
|
return offers;
|
|
} |