feat(phase7.2): media browser with server-side filters + pagination
- Migration 052: indexes on media_assets(asset_type, created_at) and products(category_key, lagertyp) for efficient filter queries - GET /api/media/assets: JOINs media_assets→products→order_lines, filters by asset_type / category_key / render_status / q (ILIKE), paginated (page/page_size), returns total+pages count - New schemas: MediaAssetBrowseItem, MediaAssetBrowseResponse - frontend/src/api/media.ts: getMediaAssets(filters), typed interfaces - MediaBrowser.tsx: rewritten with sticky filter bar (debounced search, type/category/status dropdowns), responsive grid, image previews, download buttons, pagination footer with page size selector - Renamed legacy function to listMediaAssets for backward compat Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,53 @@ export type MediaAssetType =
|
||||
| 'gltf_production'
|
||||
| 'blend_production'
|
||||
|
||||
// ── Media Browser (server-side filtered + paginated) ──────────────────────────
|
||||
|
||||
export interface MediaAssetFilters {
|
||||
asset_type?: string
|
||||
category_key?: string
|
||||
render_status?: string
|
||||
q?: string
|
||||
page?: number
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export interface MediaAssetItem {
|
||||
id: string
|
||||
asset_type: MediaAssetType
|
||||
file_path: string
|
||||
file_size_bytes: number | null
|
||||
mime_type: string | null
|
||||
created_at: string
|
||||
order_line_id: string | null
|
||||
product_id: string | null
|
||||
product_name: string | null
|
||||
product_pim_id: string | null
|
||||
category_key: string | null
|
||||
render_status: string | null
|
||||
download_url: string | null
|
||||
thumbnail_url: string | null
|
||||
}
|
||||
|
||||
export interface MediaAssetListResponse {
|
||||
items: MediaAssetItem[]
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
export function getMediaAssets(filters: MediaAssetFilters = {}): Promise<MediaAssetListResponse> {
|
||||
const params = new URLSearchParams()
|
||||
if (filters.asset_type) params.set('asset_type', filters.asset_type)
|
||||
if (filters.category_key) params.set('category_key', filters.category_key)
|
||||
if (filters.render_status) params.set('render_status', filters.render_status)
|
||||
if (filters.q) params.set('q', filters.q)
|
||||
if (filters.page !== undefined) params.set('page', String(filters.page))
|
||||
if (filters.page_size !== undefined) params.set('page_size', String(filters.page_size))
|
||||
return api.get(`/media/assets?${params}`).then(r => r.data)
|
||||
}
|
||||
|
||||
export interface MediaAsset {
|
||||
id: string
|
||||
tenant_id: string | null
|
||||
@@ -42,7 +89,7 @@ export interface MediaFilter {
|
||||
sort_dir?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
export const getMediaAssets = (filters: MediaFilter = {}): Promise<MediaAsset[]> => {
|
||||
export const listMediaAssets = (filters: MediaFilter = {}): Promise<MediaAsset[]> => {
|
||||
const params = new URLSearchParams()
|
||||
if (filters.product_id) params.set('product_id', filters.product_id)
|
||||
if (filters.order_line_id) params.set('order_line_id', filters.order_line_id)
|
||||
|
||||
Reference in New Issue
Block a user