fix(pos): handle expired API sessions and add receipt preview close button

This commit is contained in:
Deploy
2026-08-26 07:05:02 +02:00
parent b6af852b54
commit 290ced173b
14 changed files with 52 additions and 17 deletions

View File

@@ -1 +1 @@
0.2.8 0.2.9

View File

@@ -1,12 +1,12 @@
{ {
"name": "@mercadodevida/admin", "name": "@mercadodevida/admin",
"version": "0.2.8", "version": "0.2.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@mercadodevida/admin", "name": "@mercadodevida/admin",
"version": "0.2.8", "version": "0.2.9",
"dependencies": { "dependencies": {
"@lexical/history": "^0.49.0", "@lexical/history": "^0.49.0",
"@lexical/html": "^0.49.0", "@lexical/html": "^0.49.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mercadodevida/admin", "name": "@mercadodevida/admin",
"version": "0.2.8", "version": "0.2.9",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --port 3001", "dev": "next dev --port 3001",

View File

@@ -1,12 +1,12 @@
{ {
"name": "mercadodevida-pos", "name": "mercadodevida-pos",
"version": "0.2.8", "version": "0.2.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mercadodevida-pos", "name": "mercadodevida-pos",
"version": "0.2.8", "version": "0.2.9",
"dependencies": { "dependencies": {
"next": "^16.3.1", "next": "^16.3.1",
"react": "^19.2.8", "react": "^19.2.8",

View File

@@ -1,6 +1,6 @@
{ {
"name": "mercadodevida-pos", "name": "mercadodevida-pos",
"version": "0.2.8", "version": "0.2.9",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --port 3002", "dev": "next dev --port 3002",

View File

@@ -59,7 +59,15 @@ export default function ReceiptModal({
aria-modal="true" aria-modal="true"
aria-labelledby="receipt-title" aria-labelledby="receipt-title"
> >
<div className="mx-auto w-full max-w-xl rounded-2xl bg-white p-6 shadow-2xl print:max-w-none print:rounded-none print:p-0 print:shadow-none"> <div className="relative mx-auto w-full max-w-xl rounded-2xl bg-white p-6 shadow-2xl print:max-w-none print:rounded-none print:p-0 print:shadow-none">
<button
type="button"
onClick={onDelivered}
aria-label="Cerrar"
className="no-print absolute right-4 top-4 text-2xl text-gray-400 hover:text-gray-700"
>
</button>
{/* Ticket content - only this gets printed */} {/* Ticket content - only this gets printed */}
<div ref={articleRef} className="ticket-print-area"> <div ref={articleRef} className="ticket-print-area">
<article className="space-y-4 bg-white text-sm text-gray-900 print:bg-white"> <article className="space-y-4 bg-white text-sm text-gray-900 print:bg-white">

View File

@@ -12,6 +12,15 @@ async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
...(init?.headers ?? {}), ...(init?.headers ?? {}),
}, },
}); });
if (res.status === 401) {
if (typeof window !== 'undefined') {
window.location.href = '/login';
}
throw Object.assign(new Error('Sesión caducada, identifícate de nuevo'), {
status: 401,
code: 'UNAUTHORIZED',
});
}
if (!res.ok) { if (!res.ok) {
const err = (await res.json().catch(() => ({ message: res.statusText }))) as { const err = (await res.json().catch(() => ({ message: res.statusText }))) as {
message?: string; message?: string;
@@ -22,6 +31,15 @@ async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
code: err.error?.code, code: err.error?.code,
}); });
} }
// Defensive: if a proxy/misconfiguration ever returns HTML on a successful
// response, surface a readable message instead of a JSON parse crash.
const contentType = res.headers.get('content-type') ?? '';
if (!contentType.includes('application/json')) {
throw Object.assign(new Error('Respuesta inesperada del servidor (sesión o proxy)'), {
status: res.status,
code: 'INVALID_RESPONSE',
});
}
return res.json() as Promise<T>; return res.json() as Promise<T>;
} }

View File

@@ -20,6 +20,15 @@ export function proxy(request: NextRequest) {
cookie.includes('session_token='); cookie.includes('session_token=');
if (!hasSession) { if (!hasSession) {
// API callers expect JSON. Redirecting them to /login makes fetch() follow
// the redirect and receive the HTML login page, which surfaces as the
// cryptic "Unexpected token '<'" parse error in the terminal UI.
if (pathname.startsWith('/api/')) {
return NextResponse.json(
{ error: { statusCode: 401, code: 'UNAUTHORIZED', message: 'Authentication required' } },
{ status: 401 },
);
}
return NextResponse.redirect(new URL('/login', request.url)); return NextResponse.redirect(new URL('/login', request.url));
} }

View File

@@ -1,12 +1,12 @@
{ {
"name": "frontend", "name": "frontend",
"version": "0.2.8", "version": "0.2.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "frontend", "name": "frontend",
"version": "0.2.8", "version": "0.2.9",
"dependencies": { "dependencies": {
"next": "16.3.1", "next": "16.3.1",
"react": "19.2.8", "react": "19.2.8",

View File

@@ -1,6 +1,6 @@
{ {
"name": "frontend", "name": "frontend",
"version": "0.2.8", "version": "0.2.9",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -1,12 +1,12 @@
{ {
"name": "mercadodevida-backend", "name": "mercadodevida-backend",
"version": "0.2.8", "version": "0.2.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mercadodevida-backend", "name": "mercadodevida-backend",
"version": "0.2.8", "version": "0.2.9",
"dependencies": { "dependencies": {
"@fastify/cookie": "^11.1.2", "@fastify/cookie": "^11.1.2",
"@fastify/cors": "^11.3.0", "@fastify/cors": "^11.3.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "mercadodevida-backend", "name": "mercadodevida-backend",
"version": "0.2.8", "version": "0.2.9",
"private": true, "private": true,
"type": "module", "type": "module",
"description": "mercadodevida vNext backend - modular monolith skeleton", "description": "mercadodevida vNext backend - modular monolith skeleton",

View File

@@ -1,12 +1,12 @@
{ {
"name": "mercadodevida-storefront", "name": "mercadodevida-storefront",
"version": "0.2.8", "version": "0.2.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mercadodevida-storefront", "name": "mercadodevida-storefront",
"version": "0.2.8", "version": "0.2.9",
"dependencies": { "dependencies": {
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"next": "^16.0.5", "next": "^16.0.5",

View File

@@ -1,6 +1,6 @@
{ {
"name": "mercadodevida-storefront", "name": "mercadodevida-storefront",
"version": "0.2.8", "version": "0.2.9",
"private": true, "private": true,
"type": "module", "type": "module",
"description": "mercadodevida customer storefront shell", "description": "mercadodevida customer storefront shell",