feat: Phase 1 - Improve shipping zone UI (remove redundancy)

Implemented modern, Shopify-inspired shipping interface improvements.

Changes:
 Removed redundant "Settings" button from zone cards
 Added subtle Edit icon button for zone management
 Enhanced modal to be informational (not just toggles)
 Removed duplicate toggles from modal (use inline toggles instead)
 Added zone order display with context
 Show Active/Inactive badges instead of toggles in modal
 Better visual hierarchy and spacing
 Improved mobile drawer layout
 Changed "Close" to "Done" (better UX)
 Changed "Advanced Settings" to "Edit in WooCommerce"

Modal Now Shows:
- Zone name and regions in header
- Zone order with explanation
- All shipping methods with:
  * Method name and icon
  * Cost display
  * Active/Inactive status badge
  * Description (if available)
- Link to edit in WooCommerce

User Flow:
1. See zones with inline toggles (quick enable/disable)
2. Click Edit icon → View zone details
3. See all methods and their status
4. Click "Edit in WooCommerce" for advanced settings

Result: Clean, modern UI with no redundancy 
This commit is contained in:
dwindown
2025-11-09 17:10:07 +07:00
parent a1779ebbdf
commit 273ac01d54

View File

@@ -119,7 +119,7 @@ export default function ShippingPage() {
key={zone.id}
className="border rounded-lg p-3 md:p-4 hover:border-primary/50 transition-colors"
>
<div className="flex flex-col md:flex-row items-start md:justify-between gap-3 mb-3 md:mb-4">
<div className="flex items-start justify-between gap-3 mb-3 md:mb-4">
<div className="flex items-start gap-2 md:gap-3 flex-1">
<div className="p-1.5 md:p-2 bg-primary/10 rounded-lg text-primary flex-shrink-0">
<Globe className="h-4 w-4 md:h-5 md:w-5" />
@@ -127,27 +127,23 @@ export default function ShippingPage() {
<div className="min-w-0 flex-1">
<h3 className="font-semibold text-base md:text-lg">{zone.name}</h3>
<p className="text-xs md:text-sm text-muted-foreground truncate">
Regions: {zone.regions}
{zone.regions}
</p>
<p className="text-xs md:text-sm text-muted-foreground">
Rates: {zone.rates.length} shipping rate{zone.rates.length !== 1 ? 's' : ''}
{zone.rates.length} {zone.rates.length === 1 ? 'method' : 'methods'}
</p>
</div>
</div>
<div className="flex items-center gap-2 w-full md:w-auto">
<Button
variant="outline"
size="sm"
className="w-full md:w-auto"
onClick={() => {
setSelectedZone(zone);
setIsModalOpen(true);
}}
>
<Settings className="h-4 w-4 mr-2" />
{__('Settings')}
</Button>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => {
setSelectedZone(zone);
setIsModalOpen(true);
}}
>
<Edit className="h-4 w-4" />
</Button>
</div>
{/* Shipping Rates */}
@@ -224,62 +220,81 @@ export default function ShippingPage() {
{selectedZone && (
isDesktop ? (
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
<DialogContent className="max-w-3xl max-h-[90vh] flex flex-col p-0">
<DialogContent className="max-w-2xl max-h-[90vh] flex flex-col p-0">
<DialogHeader className="px-6 py-4 border-b">
<DialogTitle>{selectedZone.name} {__('Settings')}</DialogTitle>
<DialogTitle>{selectedZone.name}</DialogTitle>
<p className="text-sm text-muted-foreground mt-1">
{selectedZone.regions}
</p>
</DialogHeader>
<div className="flex-1 overflow-y-auto p-6 min-h-0">
<p className="text-sm text-muted-foreground mb-4">
{__('Configure shipping zone and methods. For advanced settings, use WooCommerce.')}
</p>
<div className="space-y-4">
<div>
<h4 className="font-medium mb-2">{__('Zone Information')}</h4>
<p className="text-sm text-muted-foreground">
{__('Name')}: {selectedZone.name}
</p>
<p className="text-sm text-muted-foreground">
{__('Regions')}: {selectedZone.regions}
</p>
<div className="space-y-6">
{/* Zone Summary */}
<div className="bg-muted/50 rounded-lg p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">{__('Zone Order')}</p>
<p className="text-xs text-muted-foreground">{__('Priority in shipping calculations')}</p>
</div>
<span className="text-2xl font-bold text-muted-foreground">{selectedZone.order}</span>
</div>
</div>
{/* Shipping Methods */}
<div>
<h4 className="font-medium mb-2">{__('Shipping Methods')}</h4>
<div className="space-y-2">
<div className="flex items-center justify-between mb-3">
<h4 className="font-semibold">{__('Shipping Methods')}</h4>
<span className="text-sm text-muted-foreground">
{selectedZone.rates?.length} {selectedZone.rates?.length === 1 ? 'method' : 'methods'}
</span>
</div>
<div className="space-y-3">
{selectedZone.rates?.map((rate: any) => (
<div key={rate.id} className="border rounded-lg p-3">
<div className="flex items-center justify-between mb-2">
<span className="font-medium">{rate.name}</span>
<ToggleField
id={`modal-${selectedZone.id}-${rate.instance_id}`}
label=""
checked={rate.enabled}
onCheckedChange={(checked) => {
handleToggle(selectedZone.id, rate.instance_id, checked);
}}
disabled={togglingMethod === `${selectedZone.id}-${rate.instance_id}`}
/>
<div key={rate.id} className="border rounded-lg p-4 hover:border-primary/50 transition-colors">
<div className="flex items-start justify-between gap-3">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<Truck className="h-4 w-4 text-muted-foreground" />
<span className="font-medium" dangerouslySetInnerHTML={{ __html: rate.name }} />
</div>
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<span className="flex items-center gap-1">
<span>{__('Cost')}:</span>
<span className="font-semibold" dangerouslySetInnerHTML={{ __html: rate.price }} />
</span>
{rate.description && (
<span className="text-xs" dangerouslySetInnerHTML={{ __html: rate.description }} />
)}
</div>
</div>
<div className="flex items-center gap-2">
<span className={`text-xs px-2 py-1 rounded-full ${
rate.enabled
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
: 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'
}`}>
{rate.enabled ? __('Active') : __('Inactive')}
</span>
</div>
</div>
<p className="text-sm text-muted-foreground">
{__('Price')}: <span dangerouslySetInnerHTML={{ __html: rate.price }} />
</p>
</div>
))}
</div>
</div>
</div>
</div>
<div className="px-6 py-4 border-t flex justify-between">
<div className="px-6 py-4 border-t flex justify-between gap-3">
<Button
variant="outline"
asChild
>
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping&zone_id=${selectedZone.id}`} target="_blank" rel="noopener noreferrer">
<ExternalLink className="h-4 w-4 mr-2" />
{__('Advanced Settings in WooCommerce')}
{__('Edit in WooCommerce')}
</a>
</Button>
<Button onClick={() => setIsModalOpen(false)}>
{__('Close')}
{__('Done')}
</Button>
</div>
</DialogContent>
@@ -288,42 +303,54 @@ export default function ShippingPage() {
<Drawer open={isModalOpen} onOpenChange={setIsModalOpen}>
<DrawerContent className="max-h-[90vh] flex flex-col">
<DrawerHeader className="border-b">
<DrawerTitle>{selectedZone.name} {__('Settings')}</DrawerTitle>
<DrawerTitle>{selectedZone.name}</DrawerTitle>
<p className="text-sm text-muted-foreground mt-1">
{selectedZone.regions}
</p>
</DrawerHeader>
<div className="flex-1 overflow-y-auto px-4 py-6 min-h-0">
<p className="text-sm text-muted-foreground mb-4">
{__('Configure shipping zone and methods. For advanced settings, use WooCommerce.')}
</p>
<div className="space-y-4">
<div>
<h4 className="font-medium mb-2">{__('Zone Information')}</h4>
<p className="text-sm text-muted-foreground">
{__('Name')}: {selectedZone.name}
</p>
<p className="text-sm text-muted-foreground">
{__('Regions')}: {selectedZone.regions}
</p>
<div className="space-y-6">
{/* Zone Summary */}
<div className="bg-muted/50 rounded-lg p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">{__('Zone Order')}</p>
<p className="text-xs text-muted-foreground">{__('Priority in shipping calculations')}</p>
</div>
<span className="text-2xl font-bold text-muted-foreground">{selectedZone.order}</span>
</div>
</div>
{/* Shipping Methods */}
<div>
<h4 className="font-medium mb-2">{__('Shipping Methods')}</h4>
<div className="space-y-2">
<div className="flex items-center justify-between mb-3">
<h4 className="font-semibold">{__('Shipping Methods')}</h4>
<span className="text-sm text-muted-foreground">
{selectedZone.rates?.length} {selectedZone.rates?.length === 1 ? 'method' : 'methods'}
</span>
</div>
<div className="space-y-3">
{selectedZone.rates?.map((rate: any) => (
<div key={rate.id} className="border rounded-lg p-3">
<div className="flex items-center justify-between mb-2">
<span className="font-medium">{rate.name}</span>
<ToggleField
id={`drawer-${selectedZone.id}-${rate.instance_id}`}
label=""
checked={rate.enabled}
onCheckedChange={(checked) => {
handleToggle(selectedZone.id, rate.instance_id, checked);
}}
disabled={togglingMethod === `${selectedZone.id}-${rate.instance_id}`}
/>
<div className="flex items-start justify-between gap-3">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<Truck className="h-4 w-4 text-muted-foreground" />
<span className="font-medium text-sm" dangerouslySetInnerHTML={{ __html: rate.name }} />
</div>
<div className="text-sm text-muted-foreground">
<span>{__('Cost')}:</span>{' '}
<span className="font-semibold" dangerouslySetInnerHTML={{ __html: rate.price }} />
</div>
</div>
<span className={`text-xs px-2 py-1 rounded-full whitespace-nowrap ${
rate.enabled
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
: 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'
}`}>
{rate.enabled ? __('Active') : __('Inactive')}
</span>
</div>
<p className="text-sm text-muted-foreground">
{__('Price')}: <span dangerouslySetInnerHTML={{ __html: rate.price }} />
</p>
</div>
))}
</div>
@@ -338,11 +365,11 @@ export default function ShippingPage() {
>
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping&zone_id=${selectedZone.id}`} target="_blank" rel="noopener noreferrer">
<ExternalLink className="h-4 w-4 mr-2" />
{__('Advanced Settings in WooCommerce')}
{__('Edit in WooCommerce')}
</a>
</Button>
<Button onClick={() => setIsModalOpen(false)} className="w-full">
{__('Close')}
{__('Done')}
</Button>
</div>
</DrawerContent>