fix(pos): handle expired API sessions and add receipt preview close button
This commit is contained in:
4
project/apps/pos/package-lock.json
generated
4
project/apps/pos/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mercadodevida-pos",
|
||||
"version": "0.2.8",
|
||||
"version": "0.2.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mercadodevida-pos",
|
||||
"version": "0.2.8",
|
||||
"version": "0.2.9",
|
||||
"dependencies": {
|
||||
"next": "^16.3.1",
|
||||
"react": "^19.2.8",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mercadodevida-pos",
|
||||
"version": "0.2.8",
|
||||
"version": "0.2.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --port 3002",
|
||||
|
||||
@@ -59,7 +59,15 @@ export default function ReceiptModal({
|
||||
aria-modal="true"
|
||||
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 */}
|
||||
<div ref={articleRef} className="ticket-print-area">
|
||||
<article className="space-y-4 bg-white text-sm text-gray-900 print:bg-white">
|
||||
|
||||
@@ -12,6 +12,15 @@ async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
...(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) {
|
||||
const err = (await res.json().catch(() => ({ message: res.statusText }))) as {
|
||||
message?: string;
|
||||
@@ -22,6 +31,15 @@ async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
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>;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,15 @@ export function proxy(request: NextRequest) {
|
||||
cookie.includes('session_token=');
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user