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:
barsa 2025-11-05 16:39:53 +09:00
parent d6f7c50e7b
commit 531627a2eb
2 changed files with 93 additions and 94 deletions

5
.cursor/worktrees.json Normal file
View File

@ -0,0 +1,5 @@
{
"setup-worktree": [
"npm install"
]
}

View File

@ -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&apos;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&apos;ll always confirm with you before applying any additional charges.
</p>
</div>
</div>