Add AI status badges to show verification status on prices

- Add ai_status column to price_history table (verified/corrected/null)
- Track AI verification status through scraper and scheduler
- Display badges next to prices:
  - ✓ AI (green): AI verified the price is correct
  -  AI (orange): AI corrected an incorrect price
- Show badges on Dashboard product cards and ProductDetail page
- Add legend explaining badges in Settings when AI Verification is enabled

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
clucraft 2026-01-23 09:45:45 -05:00
parent 3d91489f12
commit ccbc188487
11 changed files with 173 additions and 23 deletions

View file

@ -324,6 +324,7 @@ function getJitterSeconds(): number {
export interface ProductWithLatestPrice extends Product {
current_price: number | null;
currency: string | null;
ai_status: AIStatus;
}
export interface SparklinePoint {
@ -340,10 +341,10 @@ export interface ProductWithSparkline extends ProductWithLatestPrice {
export const productQueries = {
findByUserId: async (userId: number): Promise<ProductWithLatestPrice[]> => {
const result = await pool.query(
`SELECT p.*, ph.price as current_price, ph.currency
`SELECT p.*, ph.price as current_price, ph.currency, ph.ai_status
FROM products p
LEFT JOIN LATERAL (
SELECT price, currency FROM price_history
SELECT price, currency, ai_status FROM price_history
WHERE product_id = p.id
ORDER BY recorded_at DESC
LIMIT 1
@ -358,10 +359,10 @@ export const productQueries = {
findByUserIdWithSparkline: async (userId: number): Promise<ProductWithSparkline[]> => {
// Get all products with current price
const productsResult = await pool.query(
`SELECT p.*, ph.price as current_price, ph.currency
`SELECT p.*, ph.price as current_price, ph.currency, ph.ai_status
FROM products p
LEFT JOIN LATERAL (
SELECT price, currency FROM price_history
SELECT price, currency, ai_status FROM price_history
WHERE product_id = p.id
ORDER BY recorded_at DESC
LIMIT 1
@ -432,10 +433,10 @@ export const productQueries = {
findById: async (id: number, userId: number): Promise<ProductWithLatestPrice | null> => {
const result = await pool.query(
`SELECT p.*, ph.price as current_price, ph.currency
`SELECT p.*, ph.price as current_price, ph.currency, ph.ai_status
FROM products p
LEFT JOIN LATERAL (
SELECT price, currency FROM price_history
SELECT price, currency, ai_status FROM price_history
WHERE product_id = p.id
ORDER BY recorded_at DESC
LIMIT 1
@ -553,11 +554,14 @@ export const productQueries = {
};
// Price History types and queries
export type AIStatus = 'verified' | 'corrected' | null;
export interface PriceHistory {
id: number;
product_id: number;
price: number;
currency: string;
ai_status: AIStatus;
recorded_at: Date;
}
@ -586,13 +590,14 @@ export const priceHistoryQueries = {
create: async (
productId: number,
price: number,
currency: string = 'USD'
currency: string = 'USD',
aiStatus: AIStatus = null
): Promise<PriceHistory> => {
const result = await pool.query(
`INSERT INTO price_history (product_id, price, currency)
VALUES ($1, $2, $3)
`INSERT INTO price_history (product_id, price, currency, ai_status)
VALUES ($1, $2, $3, $4)
RETURNING *`,
[productId, price, currency]
[productId, price, currency, aiStatus]
);
return result.rows[0];
},