diff --git a/client/src/pages/project-detail/item-thumbnail.tsx b/client/src/pages/project-detail/item-thumbnail.tsx new file mode 100644 index 0000000..706f9f4 --- /dev/null +++ b/client/src/pages/project-detail/item-thumbnail.tsx @@ -0,0 +1,38 @@ +import { useState } from 'react'; + +interface ItemThumbnailProps { + url: string | null; + alt: string; + size?: 'sm' | 'md'; +} + +const sizeClassMap: Record['size'], string> = { + sm: 'h-14 w-14', + md: 'h-20 w-20' +}; + +export function ItemThumbnail({ url, alt, size = 'sm' }: ItemThumbnailProps) { + const [hasError, setHasError] = useState(false); + const hasImage = !!url && !hasError; + const sizeClasses = sizeClassMap[size]; + + return ( +
+ {hasImage ? ( + {alt} setHasError(true)} + /> + ) : ( +
+ No Image +
+ )} +
+ ); +} diff --git a/server/src/db/migrations/0003-create-urls-table.ts b/server/src/db/migrations/0003-create-urls-table.ts index 3dc876f..e784d29 100644 --- a/server/src/db/migrations/0003-create-urls-table.ts +++ b/server/src/db/migrations/0003-create-urls-table.ts @@ -9,7 +9,7 @@ export const migration: Migration = { CREATE TABLE IF NOT EXISTS urls ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), url TEXT NOT NULL UNIQUE, - note TEXT, + body_text TEXT, attributes JSONB NOT NULL DEFAULT '{}'::jsonb, has_price BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), diff --git a/server/src/db/urls-repository.ts b/server/src/db/urls-repository.ts index 47d6e63..7906a62 100644 --- a/server/src/db/urls-repository.ts +++ b/server/src/db/urls-repository.ts @@ -3,7 +3,7 @@ import { query } from './pool.js'; export interface UrlRow { id: string; url: string; - note: string | null; + body_text: string | null; attributes: unknown; has_price: boolean; created_at: Date; @@ -13,7 +13,7 @@ export interface UrlRow { export interface UrlRecord { id: string; url: string; - note: string | null; + bodyText: string | null; attributes: unknown; hasPrice: boolean; createdAt: string; @@ -23,7 +23,7 @@ export interface UrlRecord { const mapUrlRow = (row: UrlRow): UrlRecord => ({ id: row.id, url: row.url, - note: row.note, + bodyText: row.body_text, attributes: row.attributes ?? {}, hasPrice: row.has_price, createdAt: row.created_at.toISOString(), @@ -33,7 +33,7 @@ const mapUrlRow = (row: UrlRow): UrlRecord => ({ export const findUrlById = async (id: string): Promise => { const result = await query( ` - SELECT id, url, note, attributes, has_price, created_at, updated_at + SELECT id, url, body_text, attributes, has_price, created_at, updated_at FROM urls WHERE id = $1 `, @@ -50,7 +50,7 @@ export const findUrlById = async (id: string): Promise => { export const findUrlByValue = async (url: string): Promise => { const result = await query( ` - SELECT id, url, note, attributes, has_price, created_at, updated_at + SELECT id, url, body_text, attributes, has_price, created_at, updated_at FROM urls WHERE url = $1 `, @@ -66,7 +66,7 @@ export const findUrlByValue = async (url: string): Promise => export interface CreateUrlParams { url: string; - note?: string | null; + bodyText?: string | null; attributes?: unknown; hasPrice?: boolean; } @@ -74,15 +74,15 @@ export interface CreateUrlParams { export const createUrl = async (params: CreateUrlParams): Promise => { const result = await query( ` - INSERT INTO urls (url, note, attributes, has_price) + INSERT INTO urls (url, body_text, attributes, has_price) VALUES ($1, $2, $3, $4) ON CONFLICT (url) DO UPDATE SET updated_at = NOW() - RETURNING id, url, note, attributes, has_price, created_at, updated_at + RETURNING id, url, body_text, attributes, has_price, created_at, updated_at `, [ params.url, - params.note ?? null, + params.bodyText ?? null, params.attributes ?? {}, params.hasPrice ?? false ]