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