mirror of
https://github.com/clucraft/PriceGhost.git
synced 2026-05-06 14:22:48 +02:00
Add target price alerts, historical low indicator, bulk actions, and dashboard summary
Features: - Target price alerts: Set a specific price target and get notified when reached - Historical low indicator: Badge showing when current price is at/near all-time low - Bulk actions: Select multiple products to delete at once - Dashboard summary: Shows total products, items at lowest price, at target, biggest drops Backend changes: - Add target_price column to products table - Add target_price notification type with Telegram/Discord support - Include min_price in product queries for historical low detection - Update scheduler to check target price conditions Frontend changes: - Add target price input to ProductDetail notification settings - Show target price badge on product cards - Add "Lowest Price" and "Near Low" badges to product cards - Add bulk selection mode with checkboxes - Add dashboard summary cards at top of product list Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2acc47c21c
commit
a85e22d8bc
9 changed files with 454 additions and 5 deletions
|
|
@ -200,6 +200,7 @@ export interface Product {
|
|||
next_check_at: Date | null;
|
||||
stock_status: StockStatus;
|
||||
price_drop_threshold: number | null;
|
||||
target_price: number | null;
|
||||
notify_back_in_stock: boolean;
|
||||
created_at: Date;
|
||||
}
|
||||
|
|
@ -222,6 +223,7 @@ export interface SparklinePoint {
|
|||
export interface ProductWithSparkline extends ProductWithLatestPrice {
|
||||
sparkline: SparklinePoint[];
|
||||
price_change_7d: number | null;
|
||||
min_price: number | null;
|
||||
}
|
||||
|
||||
export const productQueries = {
|
||||
|
|
@ -272,6 +274,15 @@ export const productQueries = {
|
|||
[productIds]
|
||||
);
|
||||
|
||||
// Get min prices for all products (all-time low)
|
||||
const minPriceResult = await pool.query(
|
||||
`SELECT product_id, MIN(price) as min_price
|
||||
FROM price_history
|
||||
WHERE product_id = ANY($1)
|
||||
GROUP BY product_id`,
|
||||
[productIds]
|
||||
);
|
||||
|
||||
// Group sparkline data by product
|
||||
const sparklineMap = new Map<number, SparklinePoint[]>();
|
||||
for (const row of sparklineResult.rows) {
|
||||
|
|
@ -280,6 +291,12 @@ export const productQueries = {
|
|||
sparklineMap.set(row.product_id, points);
|
||||
}
|
||||
|
||||
// Map min prices by product
|
||||
const minPriceMap = new Map<number, number>();
|
||||
for (const row of minPriceResult.rows) {
|
||||
minPriceMap.set(row.product_id, parseFloat(row.min_price));
|
||||
}
|
||||
|
||||
// Combine products with sparkline data
|
||||
return products.map((product: ProductWithLatestPrice) => {
|
||||
const sparkline = sparklineMap.get(product.id) || [];
|
||||
|
|
@ -297,6 +314,7 @@ export const productQueries = {
|
|||
...product,
|
||||
sparkline,
|
||||
price_change_7d: priceChange7d,
|
||||
min_price: minPriceMap.get(product.id) || null,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
|
@ -344,6 +362,7 @@ export const productQueries = {
|
|||
name?: string;
|
||||
refresh_interval?: number;
|
||||
price_drop_threshold?: number | null;
|
||||
target_price?: number | null;
|
||||
notify_back_in_stock?: boolean;
|
||||
}
|
||||
): Promise<Product | null> => {
|
||||
|
|
@ -363,6 +382,10 @@ export const productQueries = {
|
|||
fields.push(`price_drop_threshold = $${paramIndex++}`);
|
||||
values.push(updates.price_drop_threshold);
|
||||
}
|
||||
if (updates.target_price !== undefined) {
|
||||
fields.push(`target_price = $${paramIndex++}`);
|
||||
values.push(updates.target_price);
|
||||
}
|
||||
if (updates.notify_back_in_stock !== undefined) {
|
||||
fields.push(`notify_back_in_stock = $${paramIndex++}`);
|
||||
values.push(updates.notify_back_in_stock);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue