93 lines
3.5 KiB
TypeScript
93 lines
3.5 KiB
TypeScript
import type { AmazonData } from './types';
|
|
|
|
function StarRating({ rating, reviewCount }: { rating: number; reviewCount?: number }) {
|
|
const full = Math.floor(rating);
|
|
const half = rating - full >= 0.5;
|
|
const stars: string[] = [];
|
|
for (let i = 0; i < full; i++) stars.push('\u2605');
|
|
if (half) stars.push('\u2606');
|
|
return (
|
|
<span className="text-amber-400 text-sm">
|
|
{stars.join('')} {rating.toFixed(1)}
|
|
{reviewCount != null && <span className="text-slate-400 text-xs ml-1">({reviewCount.toLocaleString()}件)</span>}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export function AmazonProductsDetail({ data }: { data: AmazonData }) {
|
|
const { query, products } = data;
|
|
|
|
return (
|
|
<div className="p-6">
|
|
<h2 className="text-lg font-bold text-slate-800 mb-4">
|
|
🛒 Amazon 検索結果: 「{query}」
|
|
</h2>
|
|
|
|
<div className="space-y-6">
|
|
{products.map((p, i) => (
|
|
<div key={p.asin} className="bg-white border border-slate-200 rounded-xl p-4">
|
|
<div className="flex gap-4 flex-col sm:flex-row">
|
|
{/* Product image */}
|
|
<div className="w-full sm:w-40 h-40 bg-slate-50 rounded-lg flex items-center justify-center flex-shrink-0 overflow-hidden">
|
|
{p.imageUrl ? (
|
|
<img src={p.imageUrl} alt={p.title} className="max-h-full max-w-full object-contain" />
|
|
) : (
|
|
<span className="text-4xl">💾</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* Product info */}
|
|
<div className="flex-1 min-w-0">
|
|
<div className="text-slate-400 mb-1" style={{ fontSize: 13 }}>#{i + 1}</div>
|
|
<h3 className="text-sm font-semibold text-slate-800 leading-snug mb-2">{p.title}</h3>
|
|
|
|
{p.price && (
|
|
<div className="text-xl font-bold text-red-600 mb-1">{p.price}</div>
|
|
)}
|
|
|
|
{p.rating != null && (
|
|
<div className="mb-2">
|
|
<StarRating rating={p.rating} reviewCount={p.reviewCount} />
|
|
</div>
|
|
)}
|
|
|
|
<div className="text-xs text-slate-400 mb-3">ASIN: {p.asin}</div>
|
|
|
|
<div className="flex gap-2 flex-wrap">
|
|
<a
|
|
href={p.productUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center gap-1 px-3 py-1.5 bg-amber-400 hover:bg-amber-500 text-slate-900 text-xs font-semibold rounded-lg no-underline transition-colors"
|
|
>
|
|
Amazon で見る
|
|
</a>
|
|
<a
|
|
href={p.keepaDetailUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center gap-1 px-3 py-1.5 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-semibold rounded-lg no-underline transition-colors"
|
|
>
|
|
Keepa で見る
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Keepa price graph */}
|
|
<div className="mt-4 bg-slate-50 rounded-lg p-3">
|
|
<div className="text-xs text-slate-500 mb-2">📈 価格推移 (Keepa)</div>
|
|
<img
|
|
src={p.keepaGraphUrl}
|
|
alt={`${p.title} 価格推移`}
|
|
className="w-full rounded"
|
|
loading="lazy"
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|