Refactor OrderDetail component for improved layout and readability
- Simplified the header section by restructuring the layout for better clarity and responsiveness. - Enhanced the display of order details, including service label and pricing, with improved styling. - Updated status display to utilize a more consistent design and added support for next actions. - Adjusted item display layout for better alignment and readability, including icon and title adjustments. - Improved overall spacing and typography for a cleaner user interface.
This commit is contained in:
parent
d6f7c50e7b
commit
531627a2eb
5
.cursor/worktrees.json
Normal file
5
.cursor/worktrees.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"setup-worktree": [
|
||||
"npm install"
|
||||
]
|
||||
}
|
||||
@ -329,69 +329,60 @@ export function OrderDetailContainer() {
|
||||
<div className="rounded-3xl border border-slate-200 bg-white shadow-sm">
|
||||
{/* Header Section */}
|
||||
<div className="border-b border-slate-200 bg-gradient-to-br from-white to-slate-50 px-6 py-6 sm:px-8">
|
||||
<div className="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-blue-50 text-blue-600">
|
||||
{serviceIcon}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">{serviceLabel}</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
Order #{data.orderNumber || String(data.id).slice(-8)}
|
||||
{placedDate ? ` • ${placedDate}` : null}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Row 1: Title, Date, Pricing */}
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h2 className="text-2xl font-bold text-gray-900">{serviceLabel}</h2>
|
||||
{placedDate && (
|
||||
<p className="text-sm text-gray-500">{placedDate}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{statusDescriptor && (
|
||||
<div className="flex flex-wrap items-center gap-3 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-3 text-sm text-blue-900">
|
||||
<StatusPill label={statusDescriptor.label} variant={statusPillVariant} />
|
||||
<span className="font-semibold text-blue-900">{statusDescriptor.description}</span>
|
||||
{statusDescriptor.timeline && (
|
||||
<span className="flex items-center gap-1 text-xs text-blue-800">
|
||||
<ClockIcon className="h-4 w-4" aria-hidden />
|
||||
{statusDescriptor.timeline}
|
||||
</span>
|
||||
)}
|
||||
{statusDescriptor.nextAction && (
|
||||
<span className="text-xs text-blue-700">
|
||||
Next: {statusDescriptor.nextAction}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* Pricing Section */}
|
||||
<div className="flex items-start gap-6 sm:gap-8">
|
||||
{totals.monthlyTotal > 0 && (
|
||||
<div className="text-right">
|
||||
<p className="mb-1 text-xs font-medium uppercase tracking-wider text-blue-600">Monthly</p>
|
||||
<p className="text-3xl font-bold text-gray-900">
|
||||
{yenFormatter.format(totals.monthlyTotal)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{totals.oneTimeTotal > 0 && (
|
||||
<div className="text-right">
|
||||
<p className="mb-1 text-xs font-medium uppercase tracking-wider text-blue-600">One-Time</p>
|
||||
<p className="text-3xl font-bold text-gray-900">
|
||||
{yenFormatter.format(totals.oneTimeTotal)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing Section */}
|
||||
<div className="flex items-center gap-6">
|
||||
{totals.monthlyTotal > 0 && (
|
||||
<div className="text-right">
|
||||
<p className="text-xs font-medium uppercase tracking-wider text-blue-600">Monthly</p>
|
||||
<p className="text-3xl font-bold text-gray-900">
|
||||
{yenFormatter.format(totals.monthlyTotal)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{totals.oneTimeTotal > 0 && (
|
||||
<div className="text-right">
|
||||
<p className="text-xs font-medium uppercase tracking-wider text-blue-600">One-Time</p>
|
||||
<p className="text-2xl font-bold text-gray-900">
|
||||
{yenFormatter.format(totals.oneTimeTotal)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Row 2: Status */}
|
||||
{statusDescriptor && (
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<StatusPill
|
||||
label={statusDescriptor.label}
|
||||
variant={statusPillVariant}
|
||||
size="lg"
|
||||
className="px-5 py-2 text-base font-bold"
|
||||
/>
|
||||
{statusDescriptor.nextAction && (
|
||||
<span className="text-sm text-blue-700">{statusDescriptor.nextAction}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-6 sm:px-8">
|
||||
<div className="flex flex-col gap-5">
|
||||
{/* Status Section */}
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Order Items Section */}
|
||||
<div>
|
||||
<h3 className="mb-4 text-sm font-semibold uppercase tracking-wide text-gray-700">Order Details</h3>
|
||||
<div className="space-y-2">
|
||||
<h3 className="mb-2 text-xs font-semibold uppercase tracking-widest text-gray-700" style={{ letterSpacing: '0.1em' }}>Order Details</h3>
|
||||
<div className="space-y-4">
|
||||
{displayItems.length === 0 ? (
|
||||
<div className="rounded-xl border border-dashed border-slate-200 bg-slate-50 px-4 py-8 text-center text-sm text-gray-500">
|
||||
No items found on this order.
|
||||
@ -407,7 +398,7 @@ export function OrderDetailContainer() {
|
||||
return (
|
||||
<div key={item.id}>
|
||||
{showBundleStart && (
|
||||
<div className="mb-2 flex items-center gap-2 px-1">
|
||||
<div className="mb-2 mt-3 flex items-center gap-2 px-1">
|
||||
<span className="text-[11px] font-medium uppercase tracking-wider text-purple-600">
|
||||
Bundled
|
||||
</span>
|
||||
@ -416,49 +407,54 @@ export function OrderDetailContainer() {
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col gap-3 rounded-xl border p-4 sm:flex-row sm:items-center sm:justify-between",
|
||||
style.container
|
||||
"flex flex-col gap-3 rounded-xl border px-4 py-4 sm:flex-row sm:items-center sm:justify-between",
|
||||
style.container,
|
||||
!showBundleStart && itemIndex > 0 && "border-t-0 rounded-t-none"
|
||||
)}
|
||||
>
|
||||
{/* Icon + Title & Category | Price */}
|
||||
<div className="flex flex-1 items-start gap-3">
|
||||
<div className={cn(
|
||||
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg",
|
||||
"flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-lg",
|
||||
style.icon
|
||||
)}>
|
||||
<Icon className="h-5 w-5" />
|
||||
<Icon className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h4 className="font-semibold text-gray-900">
|
||||
{item.name}
|
||||
</h4>
|
||||
<p className="mt-0.5 text-xs text-gray-500">
|
||||
{categoryConfig.label}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-1 text-sm text-gray-600 sm:items-end sm:text-right">
|
||||
{item.charges.map((charge, index) => {
|
||||
const descriptor = describeCharge(charge);
|
||||
if (charge.amount > 0) {
|
||||
return (
|
||||
<div key={`${item.id}-charge-${index}`} className="flex items-baseline gap-2">
|
||||
<span className="font-semibold text-gray-900">
|
||||
{yenFormatter.format(charge.amount)}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">{descriptor}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<div className="flex flex-1 items-baseline justify-between gap-4 sm:items-center">
|
||||
<div className="min-w-0 flex-1">
|
||||
<h4 className="font-semibold leading-snug text-gray-900">
|
||||
{item.name}
|
||||
</h4>
|
||||
<p className="mt-0.5 text-xs font-semibold text-gray-600">
|
||||
{categoryConfig.label}
|
||||
</p>
|
||||
</div>
|
||||
{/* Price - Right aligned */}
|
||||
<div className="flex flex-col items-end gap-0.5 text-right">
|
||||
{item.charges.map((charge, index) => {
|
||||
const descriptor = describeCharge(charge);
|
||||
if (charge.amount > 0) {
|
||||
return (
|
||||
<div key={`${item.id}-charge-${index}`} className="whitespace-nowrap text-lg">
|
||||
<span className="font-bold text-gray-900">
|
||||
{yenFormatter.format(charge.amount)}
|
||||
</span>
|
||||
<span className="ml-2 text-xs font-medium text-gray-500">{descriptor}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${item.id}-charge-${index}`}
|
||||
className="text-xs font-medium text-gray-500"
|
||||
>
|
||||
Included {descriptor}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<div
|
||||
key={`${item.id}-charge-${index}`}
|
||||
className="text-xs font-medium text-gray-500"
|
||||
>
|
||||
Included {descriptor}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -473,11 +469,9 @@ export function OrderDetailContainer() {
|
||||
<div className="flex items-start gap-3">
|
||||
<ExclamationTriangleIcon className="h-5 w-5 flex-shrink-0 text-amber-600" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-amber-900">Additional fees may apply</p>
|
||||
<p className="mt-0.5 text-xs leading-relaxed text-amber-800">
|
||||
Weekend installation, express setup, or specialised configuration work can
|
||||
add extra costs. We'll always confirm with you before applying any
|
||||
additional charges.
|
||||
<p className="text-sm font-bold text-amber-900">Additional fees may apply</p>
|
||||
<p className="mt-1 text-xs leading-relaxed text-amber-800/80">
|
||||
Weekend installation, express setup, or specialised configuration work can add extra costs. We'll always confirm with you before applying any additional charges.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user