Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 deletions apps/admin/app/dashboard/catalog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { type AdminCatalogItem, deleteCatalogItem, getCatalogItems } from 'admin
import { formatDate } from 'admin-app/lib/date';
import { queryKeys } from 'admin-app/lib/queryKeys';
import { ChevronLeft, ChevronRight, ExternalLink, Star } from 'lucide-react';
import Image from 'next/image';

const PAGE_SIZE = 50;

Expand Down Expand Up @@ -62,30 +63,47 @@ function CatalogRow({ item }: { item: AdminCatalogItem }) {
},
});

const thumbUrl = item.images?.[0] ?? null;

return (
<TableRow className="hover:bg-muted/20">
<TableCell>
<div>
<div className="flex items-center gap-1.5">
<p className="text-sm font-medium">{item.name}</p>
{item.productUrl && (
<a
href={item.productUrl}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-foreground"
>
<ExternalLink className="h-3 w-3" />
</a>
<div className="flex items-start gap-2.5">
{thumbUrl ? (
<Image
src={thumbUrl}
alt=""
width={40}
height={40}
className="rounded object-cover shrink-0 bg-muted"
/>
) : (
<div className="h-10 w-10 rounded bg-muted shrink-0" />
)}
<div>
<div className="flex items-center gap-1.5">
<p className="text-sm font-medium">{item.name}</p>
{item.productUrl && (
<a
href={item.productUrl}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-foreground"
>
<ExternalLink className="h-3 w-3" />
</a>
)}
</div>
<div className="flex items-center gap-2 mt-0.5">
{item.brand && <span className="text-xs text-muted-foreground">{item.brand}</span>}
{item.model && <span className="text-xs text-muted-foreground/60">{item.model}</span>}
</div>
{item.description && (
<p className="text-xs text-muted-foreground line-clamp-1 mt-0.5">
{item.description}
</p>
)}
</div>
<div className="flex items-center gap-2 mt-0.5">
{item.brand && <span className="text-xs text-muted-foreground">{item.brand}</span>}
{item.model && <span className="text-xs text-muted-foreground/60">{item.model}</span>}
</div>
{item.description && (
<p className="text-xs text-muted-foreground line-clamp-1 mt-0.5">{item.description}</p>
)}
</div>
</TableCell>
<TableCell>
Expand Down
72 changes: 48 additions & 24 deletions apps/admin/app/dashboard/packs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { type AdminPack, deletePack, getPacks } from 'admin-app/lib/api';
import { formatDate } from 'admin-app/lib/date';
import { queryKeys } from 'admin-app/lib/queryKeys';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import Image from 'next/image';

const PAGE_SIZE = 50;

Expand Down Expand Up @@ -57,30 +58,46 @@ function PackRow({ pack }: { pack: AdminPack }) {
return (
<TableRow className="hover:bg-muted/20">
<TableCell>
<div>
<div className="flex items-center gap-1.5">
<p className="text-sm font-medium">{pack.name}</p>
{pack.isAIGenerated && (
<Badge variant="secondary" className="text-[10px] px-1 py-0">
AI
</Badge>
)}
</div>
{pack.description && (
<p className="text-xs text-muted-foreground line-clamp-1">{pack.description}</p>
<div className="flex items-start gap-2.5">
{pack.image ? (
<Image
src={pack.image}
alt=""
width={40}
height={40}
className="rounded object-cover shrink-0 bg-muted"
/>
) : (
<div className="h-10 w-10 rounded bg-muted shrink-0" />
)}
{pack.tags && pack.tags.length > 0 && (
<div className="flex flex-wrap gap-1 mt-1">
{pack.tags.slice(0, 3).map((tag) => (
<span key={tag} className="text-[10px] text-muted-foreground bg-muted px-1 rounded">
{tag}
</span>
))}
{pack.tags.length > 3 && (
<span className="text-[10px] text-muted-foreground">+{pack.tags.length - 3}</span>
<div>
<div className="flex items-center gap-1.5">
<p className="text-sm font-medium">{pack.name}</p>
{pack.isAIGenerated && (
<Badge variant="secondary" className="text-[10px] px-1 py-0">
AI
</Badge>
)}
</div>
)}
{pack.description && (
<p className="text-xs text-muted-foreground line-clamp-1">{pack.description}</p>
)}
{pack.tags && pack.tags.length > 0 && (
<div className="flex flex-wrap gap-1 mt-1">
{pack.tags.slice(0, 3).map((tag) => (
<span
key={tag}
className="text-[10px] text-muted-foreground bg-muted px-1 rounded"
>
{tag}
</span>
))}
{pack.tags.length > 3 && (
<span className="text-[10px] text-muted-foreground">+{pack.tags.length - 3}</span>
)}
</div>
)}
</div>
</div>
</TableCell>
<TableCell>
Expand All @@ -99,9 +116,16 @@ function PackRow({ pack }: { pack: AdminPack }) {
</span>
</TableCell>
<TableCell>
<span className="text-sm text-muted-foreground">
{pack.createdAt ? formatDate(new Date(pack.createdAt)) : '—'}
</span>
<div className="space-y-0.5">
<span className="text-sm text-muted-foreground">
{pack.createdAt ? formatDate(new Date(pack.createdAt)) : '—'}
</span>
{pack.updatedAt && pack.updatedAt !== pack.createdAt && (
<p className="text-xs text-muted-foreground/60">
upd {formatDate(new Date(pack.updatedAt))}
</p>
)}
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-1">
Expand Down
13 changes: 10 additions & 3 deletions apps/admin/app/dashboard/users/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,16 @@ function UserRow({ user }: { user: AdminUser }) {
</span>
</TableCell>
<TableCell>
<span className="text-sm text-muted-foreground">
{user.createdAt ? formatDate(new Date(user.createdAt)) : '—'}
</span>
<div className="space-y-0.5">
<span className="text-sm text-muted-foreground">
{user.createdAt ? formatDate(new Date(user.createdAt)) : '—'}
</span>
{user.updatedAt && user.updatedAt !== user.createdAt && (
<p className="text-xs text-muted-foreground/60">
act {formatDate(new Date(user.updatedAt))}
</p>
)}
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-1">
Expand Down
28 changes: 24 additions & 4 deletions apps/admin/components/analytics/catalog-analytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,13 @@ export function CatalogAnalytics() {
{ label: 'Total Items', value: overview.totalItems.toLocaleString() },
{ label: 'Brands', value: overview.totalBrands.toLocaleString() },
{
label: 'Avg Price',
value: overview.avgPrice != null ? `$${overview.avgPrice.toFixed(2)}` : '—',
label: 'Price Range',
value:
overview.minPrice != null && overview.maxPrice != null
? `$${overview.minPrice.toFixed(2)}–$${overview.maxPrice.toFixed(2)}`
: overview.avgPrice != null
? `avg $${overview.avgPrice.toFixed(2)}`
: '—',
Comment on lines +209 to +215
},
{ label: 'Added Last 30d', value: overview.addedLast30Days.toLocaleString() },
].map((s) => (
Expand Down Expand Up @@ -450,7 +455,7 @@ export function CatalogAnalytics() {
<table className="w-full text-sm">
<thead>
<tr className="border-b text-muted-foreground">
<th className="pb-2 text-left font-medium">Source</th>
<th className="pb-2 text-left font-medium">Source / File</th>
<th className="pb-2 text-left font-medium">Status</th>
<th className="pb-2 text-right font-medium">Processed</th>
<th className="pb-2 text-right font-medium">Valid</th>
Expand All @@ -465,7 +470,22 @@ export function CatalogAnalytics() {
<tbody>
{etl.jobs.map((job) => (
<tr key={job.id} className="border-b last:border-0">
<td className="py-2 pr-4 font-mono text-xs">{job.source}</td>
<td className="py-2 pr-4">
<div className="font-mono text-xs">{job.source}</div>
{job.filename && (
<div
className="text-xs text-muted-foreground truncate max-w-[180px]"
title={job.filename}
>
{job.filename}
</div>
)}
{job.scraperRevision && (
<div className="text-[10px] text-muted-foreground/60 font-mono">
rev {job.scraperRevision.slice(0, 7)}
</div>
)}
</td>
<td className="py-2 pr-4">
<Badge variant={statusBadgeVariant(job.status)} className="text-xs">
{job.status}
Expand Down
Loading