Order summary
Welcome kits for the six October starters · Order NW-10482
Items
- £228.00Northwind hoodieNavy · sizes S–XL · 6 × £38.00
- £99.00Insulated bottleBrushed steel, 500 ml · 6 × £16.50
- £54.00Dotted notebookA5, recycled paper · 6 × £9.00
- £324.00Laptop backpackFits 16-inch laptops · 6 × £54.00
- £64.00Laptop standAluminium, foldable · 2 × £32.00
- Subtotal
- £769.00
- Discounts
- −£76.90
- Delivery
- £12.00
- Total (incl. VAT)
- £704.10
order-summary.tsx
"use client";
import { useState, type SyntheticEvent } from "react";
import {
LuBackpack,
LuCoffee,
LuLaptop,
LuNotebookPen,
LuShirt,
LuTag,
LuTruck,
} from "react-icons/lu";
import { Accordion, AccordionItem, Alert, Badge, Button, Input, ListItem } from "@nexera-ui/react";
const money = new Intl.NumberFormat("en-GB", { style: "currency", currency: "GBP" });
const ITEMS = [
{
id: "hoodie",
icon: <LuShirt />,
name: "Northwind hoodie",
detail: "Navy · sizes S–XL",
qty: 6,
price: 38,
},
{
id: "bottle",
icon: <LuCoffee />,
name: "Insulated bottle",
detail: "Brushed steel, 500 ml",
qty: 6,
price: 16.5,
},
{
id: "notebook",
icon: <LuNotebookPen />,
name: "Dotted notebook",
detail: "A5, recycled paper",
qty: 6,
price: 9,
},
{
id: "bag",
icon: <LuBackpack />,
name: "Laptop backpack",
detail: "Fits 16-inch laptops",
qty: 6,
price: 54,
},
{
id: "stand",
icon: <LuLaptop />,
name: "Laptop stand",
detail: "Aluminium, foldable",
qty: 2,
price: 32,
},
] as const;
const SUBTOTAL = ITEMS.reduce((sum, item) => sum + item.qty * item.price, 0);
const BULK = SUBTOTAL * 0.1;
const DELIVERY = 12;
/** Order summary: the items in a welcome-kit order, discounts, delivery and the totals. */
export default function OrderSummary() {
const [code, setCode] = useState("");
const [promo, setPromo] = useState<string | null>(null);
const [placed, setPlaced] = useState(false);
const promoAmount = promo ? 25 : 0;
const total = SUBTOTAL - BULK - promoAmount + DELIVERY;
const applyCode = (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
if (code.trim().toUpperCase() === "WELCOME25") setPromo("WELCOME25");
};
return (
<div className="@container bg-page px-4 py-8 @3xl:px-8">
<div className="mx-auto grid w-full max-w-xl gap-5 rounded-xl border border-default bg-surface p-5 shadow-xs @3xl:p-6">
<header className="flex flex-wrap items-start justify-between gap-2">
<div className="grid gap-1">
<h2 className="text-heading-h3 text-primary">Order summary</h2>
<p className="text-body-small text-secondary">
Welcome kits for the six October starters · Order NW-10482
</p>
</div>
<Badge tone="info" label="Draft" />
</header>
<section aria-labelledby="items-title" className="grid gap-2">
<h3 id="items-title" className="text-heading-h4 text-primary">
Items
</h3>
<ul className="m-0 grid list-none divide-y divide-subtle p-0">
{ITEMS.map((item) => (
<li key={item.id}>
<ListItem
icon={item.icon}
title={item.name}
subtitle={`${item.detail} · ${String(item.qty)} × ${money.format(item.price)}`}
trailing={
<span className="text-body-strong text-primary tabular-nums">
{money.format(item.qty * item.price)}
</span>
}
/>
</li>
))}
</ul>
</section>
<Accordion allowsMultipleExpanded>
<AccordionItem
id="discounts"
title="Discounts"
icon={<LuTag />}
meta={`−${money.format(BULK + promoAmount)}`}
level={3}
>
<div className="grid gap-3">
<p className="text-body-small text-secondary">
10% bulk discount applied for orders over £500.
</p>
{promo ? (
<Alert
tone="success"
title={`${promo} applied`}
description={`${money.format(promoAmount)} off this order.`}
politeness="polite"
/>
) : (
<form onSubmit={applyCode} className="flex flex-wrap items-end gap-2">
<Input
label="Promo code"
name="promo"
value={code}
onChange={setCode}
description="Try WELCOME25"
className="min-w-0 flex-1"
/>
<Button type="submit" variant="secondary">
Apply
</Button>
</form>
)}
</div>
</AccordionItem>
<AccordionItem
id="delivery"
title="Delivery"
icon={<LuTruck />}
meta={money.format(DELIVERY)}
level={3}
>
Tracked courier to Northwind London, 18 Canal Street, N1 9GU. Arrives by Friday 16
October 2026.
</AccordionItem>
</Accordion>
<dl className="grid gap-2 text-body-default">
{[
["Subtotal", money.format(SUBTOTAL)],
["Discounts", `−${money.format(BULK + promoAmount)}`],
["Delivery", money.format(DELIVERY)],
].map(([label, value]) => (
<div key={label} className="flex justify-between gap-4">
<dt className="text-secondary">{label}</dt>
<dd className="text-primary tabular-nums">{value}</dd>
</div>
))}
<div className="flex justify-between gap-4 border-t border-subtle pt-3 text-body-large">
<dt className="text-primary">Total (incl. VAT)</dt>
<dd className="text-primary tabular-nums">{money.format(total)}</dd>
</div>
</dl>
{placed ? (
<Alert
tone="success"
title="Order placed"
description="Kits ship to the London office. This is a demo."
politeness="polite"
/>
) : null}
<div className="flex flex-col-reverse gap-2 @sm:flex-row @sm:justify-end">
<Button variant="secondary" size="lg">
Edit items
</Button>
<Button
size="lg"
onPress={() => {
setPlaced(true);
}}
>
Place order
</Button>
</div>
</div>
</div>
);
}
More commerce and payments blocks
- Pricing plansMonthly or yearly plans with a recommended one.
- CheckoutCard details, billing address and order total.
- Saved payment methodsChoose, add or remove a card.
- PayslipEarnings and deductions with totals.
- Product galleryPhotos with thumbnails and zoom.
- Reviews and ratingsAverage score, breakdown and written reviews.
- Order trackingWhere an order is, step by step.