101 lines
4.5 KiB
TypeScript
101 lines
4.5 KiB
TypeScript
import React from 'react';
|
|
import type { DealItem } from '../types';
|
|
import { TagIcon } from './icons/TagIcon';
|
|
import { LoadingSpinner } from './LoadingSpinner';
|
|
import { formatUnitPrice } from '../utils/unitConverter';
|
|
import { Session } from '@supabase/supabase-js';
|
|
import { UserIcon } from './icons/UserIcon';
|
|
|
|
interface PriceChartProps {
|
|
deals: DealItem[];
|
|
isLoading: boolean;
|
|
unitSystem: 'metric' | 'imperial';
|
|
session: Session | null;
|
|
}
|
|
|
|
export const PriceChart: React.FC<PriceChartProps> = ({ deals, isLoading, unitSystem, session }) => {
|
|
const renderContent = () => {
|
|
if (!session) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center h-full min-h-[150px] text-center">
|
|
<UserIcon className="w-10 h-10 text-gray-400 mb-3" />
|
|
<h4 className="font-semibold text-gray-700 dark:text-gray-300">Personalized Deals</h4>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
|
Log in to see active deals for items on your watchlist.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex justify-center items-center h-full min-h-[100px]">
|
|
<LoadingSpinner /> <span className="ml-2 text-sm text-gray-500 dark:text-gray-400">Finding active deals...</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (deals.length === 0) {
|
|
return <p className="text-sm text-gray-500 dark:text-gray-400 text-center py-4">No deals for your watched items found in any currently valid flyers.</p>;
|
|
}
|
|
|
|
return (
|
|
<div className="overflow-y-auto max-h-[20rem]">
|
|
<table className="min-w-full text-sm">
|
|
<thead className="bg-gray-50 dark:bg-gray-800 sticky top-0 z-10">
|
|
<tr>
|
|
<th className="px-4 py-2 text-left font-medium text-gray-600 dark:text-gray-300">Item</th>
|
|
<th className="px-4 py-2 text-left font-medium text-gray-600 dark:text-gray-300">Store</th>
|
|
<th className="px-4 py-2 text-right font-medium text-gray-600 dark:text-gray-300">Price</th>
|
|
<th className="px-4 py-2 text-right font-medium text-gray-600 dark:text-gray-300">Unit Price</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
|
{deals.map((deal, index) => {
|
|
const formattedUnitPrice = formatUnitPrice(deal.unit_price, unitSystem);
|
|
return (
|
|
<tr key={`${deal.item}-${deal.storeName}-${index}`} className="hover:bg-gray-50 dark:hover:bg-gray-800/50">
|
|
<td className="px-4 py-2 font-semibold text-gray-900 dark:text-white">
|
|
<div className="flex justify-between items-baseline">
|
|
<span>{deal.item}</span>
|
|
{deal.master_item_name && deal.master_item_name.toLowerCase() !== deal.item.toLowerCase() && (
|
|
<span className="ml-2 text-xs font-normal italic text-gray-500 dark:text-gray-400 whitespace-nowrap">
|
|
({deal.master_item_name})
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="text-xs text-gray-500 dark:text-gray-400 font-normal">{deal.quantity}</div>
|
|
</td>
|
|
<td className="px-4 py-2 text-left text-gray-700 dark:text-gray-200">{deal.storeName}</td>
|
|
<td className="px-4 py-2 text-right text-gray-700 dark:text-gray-200">{deal.price_display}</td>
|
|
<td className="px-4 py-2 text-right">
|
|
<div className="flex flex-col items-end">
|
|
<span className="font-semibold text-gray-700 dark:text-gray-300">
|
|
{formattedUnitPrice.price}
|
|
</span>
|
|
{formattedUnitPrice.unit && (
|
|
<span className="text-xs text-gray-500 dark:text-gray-400">
|
|
{formattedUnitPrice.unit}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
)
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
|
|
<h3 className="text-lg font-semibold mb-4 text-gray-800 dark:text-white flex items-center">
|
|
<TagIcon className="w-5 h-5 mr-2 text-brand-primary" />
|
|
Active Deals on Watched Items
|
|
</h3>
|
|
{renderContent()}
|
|
</div>
|
|
);
|
|
}; |