Avviso storefront per ordini gia' annullati/rimborsati (info non bloccante)
Al passo 2 del recesso, se l'ordine risulta gia' annullato (cancelledAt) o rimborsato/voided (financialStatus), mostra un banner informativo ambra (non blocca): il recesso resta ammesso (diritto incondizionato + funzione sempre accessibile Art. 54-bis), ma il consumatore e' informato dello stato. - recesso.copy: NOTICE.orderClosed. - recesso.server: noticeBanner + campo notice in renderStep2 + stile .notice ambra con icona. - proxy: calcola orderClosed al lookup e passa la notice a renderStep2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mv83a29B4eFv5ixoj6PoE1
This commit is contained in:
@@ -47,6 +47,12 @@ export const ERROR = {
|
|||||||
generic: "Si è verificato un problema. Riprova tra poco.",
|
generic: "Si è verificato un problema. Riprova tra poco.",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
// Avvisi informativi (non bloccanti).
|
||||||
|
export const NOTICE = {
|
||||||
|
orderClosed:
|
||||||
|
"Questo ordine risulta già annullato o rimborsato. Puoi comunque registrare il recesso; il negozio ti contatterà per eventuali dettagli.",
|
||||||
|
} as const;
|
||||||
|
|
||||||
// Schermata finale (dopo "Conferma recesso").
|
// Schermata finale (dopo "Conferma recesso").
|
||||||
export function successMessage(
|
export function successMessage(
|
||||||
orderName: string,
|
orderName: string,
|
||||||
|
|||||||
@@ -806,6 +806,21 @@ const PAGE_CSS = `
|
|||||||
font-size: 0.92rem;
|
font-size: 0.92rem;
|
||||||
}
|
}
|
||||||
.error__icon { flex: none; width: 20px; height: 20px; margin-top: 1px; fill: currentColor; }
|
.error__icon { flex: none; width: 20px; height: 20px; margin-top: 1px; fill: currentColor; }
|
||||||
|
.notice {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
background: rgba(240, 170, 40, 0.14);
|
||||||
|
border: 1px solid rgba(240, 170, 40, 0.55);
|
||||||
|
border-left: 3px solid rgba(240, 170, 40, 0.95);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 12px 14px;
|
||||||
|
margin: 0 0 18px;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.notice__icon { flex: none; width: 20px; height: 20px; margin-top: 1px; fill: #e0a020; }
|
||||||
|
|
||||||
/* Riepilogo (step 3) */
|
/* Riepilogo (step 3) */
|
||||||
.summary { margin: 18px 0 4px; }
|
.summary { margin: 18px 0 4px; }
|
||||||
@@ -893,6 +908,14 @@ function errorBanner(message?: string): string {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function noticeBanner(message?: string): string {
|
||||||
|
if (!message) return "";
|
||||||
|
return `<div class="notice" role="status">
|
||||||
|
<svg class="notice__icon" viewBox="0 0 20 20" aria-hidden="true" focusable="false"><path d="M10 1.5a8.5 8.5 0 1 0 0 17 8.5 8.5 0 0 0 0-17ZM9 5h2v2H9V5Zm0 4h2v6H9V9Z"/></svg>
|
||||||
|
<span>${escapeHtml(message)}</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pulsante info "i" + popover nativo (mini-modal, zero JS via Popover API).
|
* Pulsante info "i" + popover nativo (mini-modal, zero JS via Popover API).
|
||||||
* Il popover vive nel top-layer; chiusura con Esc, click fuori o pulsante.
|
* Il popover vive nel top-layer; chiusura con Esc, click fuori o pulsante.
|
||||||
@@ -985,12 +1008,13 @@ export function renderStep2(data: {
|
|||||||
customerName?: string;
|
customerName?: string;
|
||||||
statementText: string;
|
statementText: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
notice?: string;
|
||||||
}): string {
|
}): string {
|
||||||
const customerName = data.customerName ?? "";
|
const customerName = data.customerName ?? "";
|
||||||
return renderShell(
|
return renderShell(
|
||||||
stepLayout({
|
stepLayout({
|
||||||
head: stepHead(2, `Ordine ${data.orderName}`),
|
head: stepHead(2, `Ordine ${data.orderName}`),
|
||||||
body: `${errorBanner(data.error)}
|
body: `${errorBanner(data.error)}${noticeBanner(data.notice)}
|
||||||
<form id="rcform" method="post" action="${PROXY_STOREFRONT_PATH}" novalidate>
|
<form id="rcform" method="post" action="${PROXY_STOREFRONT_PATH}" novalidate>
|
||||||
<input type="hidden" name="intent" value="details">
|
<input type="hidden" name="intent" value="details">
|
||||||
<input type="hidden" name="orderId" value="${attr(data.orderId)}">
|
<input type="hidden" name="orderId" value="${attr(data.orderId)}">
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ERROR,
|
ERROR,
|
||||||
EXCLUSION_REASON,
|
EXCLUSION_REASON,
|
||||||
|
NOTICE,
|
||||||
exclusionMessage,
|
exclusionMessage,
|
||||||
statementTemplate,
|
statementTemplate,
|
||||||
successMessage,
|
successMessage,
|
||||||
@@ -195,12 +196,17 @@ export const action = async ({ request }: ActionFunctionArgs) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const orderClosed =
|
||||||
|
!!match.cancelledAt ||
|
||||||
|
match.financialStatus === "REFUNDED" ||
|
||||||
|
match.financialStatus === "VOIDED";
|
||||||
return htmlResponse(
|
return htmlResponse(
|
||||||
renderStep2({
|
renderStep2({
|
||||||
orderId: match.orderId,
|
orderId: match.orderId,
|
||||||
orderName: match.orderName,
|
orderName: match.orderName,
|
||||||
email: match.email,
|
email: match.email,
|
||||||
statementText: statementTemplate(match.orderName),
|
statementText: statementTemplate(match.orderName),
|
||||||
|
notice: orderClosed ? NOTICE.orderClosed : undefined,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user