fix(storefront+TPV): storefront live search proxy, product expiry/weight display, TPV receipt popup timestamp

This commit is contained in:
chattie
2026-08-24 22:11:04 +02:00
parent 2ed2ac8cd1
commit 71165911ab
6 changed files with 79 additions and 11 deletions

View File

@@ -64,6 +64,11 @@ export default function ReceiptModal({
<div ref={articleRef} className="ticket-print-area">
<article className="space-y-4 text-sm text-gray-900">
<header className="border-b border-dashed border-gray-400 pb-4 text-center">
{/* Timestamp */}
<div className="flex justify-between items-center mb-2 text-xs text-gray-500">
<span>Ticket #{receipt.receiptNumber}</span>
<span>{new Date(receipt.createdAt).toLocaleString('es-ES', { dateStyle: 'short', timeStyle: 'short' })}</span>
</div>
{/* Logo */}
<div className="mb-3 flex justify-center">
<img

View File

@@ -1,13 +1,17 @@
/**
* Add twitter_url and pinterest_url columns to store_settings table.
* Usage: node --env-file=.env -e "require('./migrations/062_settings_twitter_pinterest.js')"
*/
import { query } from '../src/infrastructure/db/migrate.js';
async function migrate() {
await query(`ALTER TABLE store_settings ADD COLUMN IF NOT EXISTS twitter_url TEXT`);
await query(`ALTER TABLE store_settings ADD COLUMN IF NOT EXISTS pinterest_url TEXT`);
console.log('✓ Added twitter_url and pinterest_url to store_settings');
}
/** @param {import('pg-migrate').MigrationBuilder} pgm */
export const up = (pgm) => {
pgm.addColumn('store_settings', {
twitter_url: { type: 'text', notNull: false, default: null },
pinterest_url: { type: 'text', notNull: false, default: null },
});
};
migrate().catch(e => { console.error(e); process.exit(1); });
/** @param {import('pg-migrate').MigrationBuilder} pgm */
export const down = (pgm) => {
pgm.dropColumn('store_settings', 'twitter_url');
pgm.dropColumn('store_settings', 'pinterest_url');
};

View File

@@ -0,0 +1,34 @@
import { NextRequest, NextResponse } from 'next/server';
const API = process.env.NEXT_PUBLIC_API_URL ?? 'http://127.0.0.1:3000';
export async function GET(req: NextRequest) {
const path = req.nextUrl.pathname.replace('/api/catalog-proxy/', '');
const cookies = req.headers.get('cookie') ?? '';
try {
const res = await fetch(`${API}/${path}${req.nextUrl.search}`, {
headers: { accept: 'application/json', cookie: cookies },
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
} catch (err) {
return NextResponse.json({ error: String(err) }, { status: 502 });
}
}
export async function POST(req: NextRequest) {
const path = req.nextUrl.pathname.replace('/api/catalog-proxy/', '');
const cookies = req.headers.get('cookie') ?? '';
const body = await req.text();
try {
const res = await fetch(`${API}/${path}${req.nextUrl.search}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', cookie: cookies },
body,
});
const data = await res.json().catch(() => ({}));
return NextResponse.json(data, { status: res.status });
} catch (err) {
return NextResponse.json({ error: String(err) }, { status: 502 });
}
}

View File

@@ -102,6 +102,18 @@ export default async function ProductPage({ params }: PageProps) {
<dt className="font-semibold text-emerald-950">Estado</dt>
<dd>{product.state}</dd>
</div>
{product.expirationDate && (
<div>
<dt className="font-semibold text-emerald-950">Caducidad</dt>
<dd>{new Date(product.expirationDate).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' })}</dd>
</div>
)}
{product.unitWeightKg && (
<div>
<dt className="font-semibold text-emerald-950">Peso</dt>
<dd>{product.unitWeightKg >= 1 ? `${product.unitWeightKg} kg` : `${Math.round(product.unitWeightKg * 1000)} g`}</dd>
</div>
)}
</dl>
<AddToCart
productId={product.id}

View File

@@ -53,6 +53,8 @@ export interface ProductSummaryDto {
seoDescription: string | null;
categoryIds: string[];
brandId: string | null;
expirationDate?: string | null;
unitWeightKg?: number | null;
createdAt: string;
updatedAt: string;
}
@@ -79,7 +81,11 @@ export interface SearchProductsInput {
// Use env var if set, otherwise use relative URL (works for SSR and client-side via same-origin)
function apiBaseUrl(): string {
const base = process.env.API_BASE_URL ?? process.env.NEXT_PUBLIC_API_BASE_URL ?? '';
// Browser: use same-origin proxy to avoid network/IP issues
// SSR: use backend URL directly
const base = (typeof window !== 'undefined')
? '/api/catalog-proxy'
: (process.env.API_BASE_URL ?? process.env.NEXT_PUBLIC_API_BASE_URL ?? 'http://127.0.0.1:3000');
return base.replace(/\/$/, '');
}

View File

@@ -1,12 +1,12 @@
{
"feature_id": "ADMIN-UI-FIXES2",
"feature_id": "FRONTEND-UI-FIXES2",
"stage": "build",
"agent": "implementer",
"action": "",
"state": "running",
"next_agent": "leader",
"waiting_for": "Seleccionar una feature pending y actualizar este estado",
"updated_at": "2026-08-24T20:06:08Z",
"updated_at": "2026-08-24T20:09:36Z",
"timeline": [
{
"ts": "2026-08-24T20:06:08Z",
@@ -14,6 +14,13 @@
"stage": "build",
"state": "running",
"message": "Estado actualizado"
},
{
"ts": "2026-08-24T20:09:36Z",
"agent": "implementer",
"stage": "build",
"state": "running",
"message": "Estado actualizado"
}
]
}