74 lines
2.6 KiB
Markdown
74 lines
2.6 KiB
Markdown
# F-076 — Architect: Link to frontend product + archive separate listing
|
|
|
|
## Current state
|
|
|
|
Admin products listing (`/products`) shows all products (active, archived, draft) in one table. No link to the frontend product page. No way to filter by state.
|
|
|
|
## Goal
|
|
|
|
1. **Frontend link**: Add a column with an external link icon that opens the product on the frontend (`http://192.168.18.93:3003/productos/[slug]`). Only shown for `state === 'active'` products.
|
|
2. **Archive listing**: Add a status filter (Todos / Activos / Archivados) to separate active from archived products.
|
|
|
|
## Scope IN
|
|
|
|
- `project/apps/admin/src/app/(dashboard)/products/page.tsx`:
|
|
- Add `statusFilter` state: `'all' | 'active' | 'archived'`
|
|
- Add filter buttons (Todos / Activos / Archivados) below the search bar
|
|
- Add a new column "Tienda" (Frontend) with an external link icon for active products only
|
|
- Filter `products` by `p.state` when `statusFilter !== 'all'`
|
|
- Update total count to reflect filtered products
|
|
- Use external link URL: `http://192.168.18.93:3003/productos/[slug]`
|
|
|
|
## Scope OUT
|
|
|
|
- No backend changes
|
|
- No new pages (filter is inline in the same page)
|
|
- No change to the product editor
|
|
|
|
## Design
|
|
|
|
Status filter buttons:
|
|
```tsx
|
|
<div className="flex gap-2 mb-4">
|
|
{(['all', 'active', 'archived'] as const).map(f => (
|
|
<button key={f} onClick={() => setStatusFilter(f)}
|
|
className={`px-3 py-1.5 rounded-lg text-xs font-medium ${
|
|
statusFilter === f ? 'bg-[#2D6A4F] text-white' : 'bg-white border border-gray-300 text-gray-600 hover:bg-gray-50'
|
|
}`}>
|
|
{f === 'all' ? 'Todos' : f === 'active' ? 'Activos' : 'Archivados'}
|
|
</button>
|
|
))}
|
|
</div>
|
|
```
|
|
|
|
Frontend link column:
|
|
```tsx
|
|
{p.state === 'active' ? (
|
|
<a
|
|
href={`http://192.168.18.93:3003/productos/${p.slug}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
title="Ver en la tienda"
|
|
className="inline-flex items-center justify-center w-8 h-8 rounded-lg bg-green-50 text-green-600 hover:bg-green-100 transition-colors"
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
|
</svg>
|
|
</a>
|
|
) : (
|
|
<span className="w-8 h-8" />
|
|
)}
|
|
```
|
|
|
|
## Risk
|
|
|
|
- **Low risk**: purely frontend UX changes, no backend or DB changes
|
|
- URL is hardcoded to local dev URL (acceptable for admin panel)
|
|
|
|
## Verification
|
|
|
|
- `npx tsc --noEmit` admin
|
|
- `npx eslint` on changed file
|
|
- `./scripts/verify.sh` green
|