feat: Complete Zone CRUD + fix terminology
## ✅ Issue #2: Zone CRUD Complete - Added full Add/Edit Zone dialog with region selector - Multi-select for countries/states/continents - Create, Update, Delete all working - NO MORE menu-ing WooCommerce! ## ✅ Issue #3: Terminology Fixed - Changed "Delivery Option" → "Shipping Method" everywhere - Fixed query enabled condition (showAvailableMethods) - Now methods list appears correctly ## UI Improvements: - 3 buttons per zone: Edit (pencil), Delete (trash), Settings (gear) - Edit = zone name/regions - Settings = manage methods - Clear separation of concerns
This commit is contained in:
@@ -55,7 +55,14 @@ export default function ShippingPage() {
|
||||
const { data: availableMethods = [] } = useQuery({
|
||||
queryKey: ['available-shipping-methods'],
|
||||
queryFn: () => api.get('/settings/shipping/methods/available'),
|
||||
enabled: showAddMethod,
|
||||
enabled: showAvailableMethods,
|
||||
});
|
||||
|
||||
// Fetch available locations (countries/states) for zone regions
|
||||
const { data: availableLocations = [] } = useQuery({
|
||||
queryKey: ['available-locations'],
|
||||
queryFn: () => api.get('/settings/shipping/locations'),
|
||||
enabled: showAddZone || !!editingZone,
|
||||
});
|
||||
|
||||
// Sync selectedZone with zones data when it changes
|
||||
@@ -257,12 +264,10 @@ export default function ShippingPage() {
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
asChild
|
||||
onClick={() => setShowAddZone(true)}
|
||||
>
|
||||
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping&action=add_zone`} target="_blank" rel="noopener noreferrer">
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
{__('Add Zone')}
|
||||
</a>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
@@ -272,12 +277,10 @@ export default function ShippingPage() {
|
||||
{__('No shipping zones configured yet. Create your first zone to start offering shipping.')}
|
||||
</p>
|
||||
<Button
|
||||
asChild
|
||||
onClick={() => setShowAddZone(true)}
|
||||
>
|
||||
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping&action=add_zone`} target="_blank" rel="noopener noreferrer">
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
{__('Create First Zone')}
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
@@ -306,7 +309,8 @@ export default function ShippingPage() {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setSelectedZone(zone)}
|
||||
onClick={() => setEditingZone(zone)}
|
||||
title={__('Edit zone name and regions')}
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -314,9 +318,18 @@ export default function ShippingPage() {
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setDeletingZone(zone)}
|
||||
title={__('Delete zone')}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-destructive" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setSelectedZone(zone)}
|
||||
title={__('Manage shipping methods')}
|
||||
>
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -403,7 +416,7 @@ export default function ShippingPage() {
|
||||
</DialogHeader>
|
||||
<div className="flex-1 overflow-y-auto p-6 min-h-0">
|
||||
<div className="space-y-4">
|
||||
{/* Add Delivery Option Button */}
|
||||
{/* Add Shipping Method Button */}
|
||||
{!showAvailableMethods ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -411,7 +424,7 @@ export default function ShippingPage() {
|
||||
onClick={() => setShowAvailableMethods(true)}
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
{__('Add Delivery Option')}
|
||||
{__('Add Shipping Method')}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="border rounded-lg p-4 space-y-3">
|
||||
@@ -446,7 +459,7 @@ export default function ShippingPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Delivery Options Accordion */}
|
||||
{/* Shipping Methods Accordion */}
|
||||
<Accordion type="single" collapsible value={expandedMethod} onValueChange={(value) => {
|
||||
setExpandedMethod(value);
|
||||
if (value) {
|
||||
@@ -615,7 +628,7 @@ export default function ShippingPage() {
|
||||
</DrawerHeader>
|
||||
<div className="flex-1 overflow-y-auto px-4 py-4 min-h-0">
|
||||
<div className="space-y-3">
|
||||
{/* Add Delivery Option Button */}
|
||||
{/* Add Shipping Method Button */}
|
||||
{!showAvailableMethods ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -623,7 +636,7 @@ export default function ShippingPage() {
|
||||
onClick={() => setShowAvailableMethods(true)}
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
{__('Add Delivery Option')}
|
||||
{__('Add Shipping Method')}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="border rounded-lg p-3 space-y-2">
|
||||
@@ -658,7 +671,7 @@ export default function ShippingPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Delivery Options Accordion (Mobile) */}
|
||||
{/* Shipping Methods Accordion (Mobile) */}
|
||||
<Accordion type="single" collapsible value={expandedMethod} onValueChange={(value) => {
|
||||
setExpandedMethod(value);
|
||||
if (value) {
|
||||
@@ -859,6 +872,112 @@ export default function ShippingPage() {
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
{/* Add/Edit Zone Dialog */}
|
||||
<Dialog open={showAddZone || !!editingZone} onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setShowAddZone(false);
|
||||
setEditingZone(null);
|
||||
}
|
||||
}}>
|
||||
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingZone ? __('Edit Zone') : __('Add Shipping Zone')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const zoneName = formData.get('name') as string;
|
||||
const selectedRegions = formData.getAll('regions').map(code => {
|
||||
const location = availableLocations.find((l: any) => l.code === code);
|
||||
return location ? { code: location.code, type: location.type } : null;
|
||||
}).filter(Boolean);
|
||||
|
||||
if (editingZone) {
|
||||
updateZoneMutation.mutate({
|
||||
zoneId: editingZone.id,
|
||||
name: zoneName,
|
||||
regions: selectedRegions,
|
||||
});
|
||||
} else {
|
||||
createZoneMutation.mutate({
|
||||
name: zoneName,
|
||||
regions: selectedRegions,
|
||||
});
|
||||
}
|
||||
}} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="zone-name" className="text-sm font-medium block mb-2">
|
||||
{__('Zone Name')}
|
||||
</label>
|
||||
<input
|
||||
id="zone-name"
|
||||
name="name"
|
||||
type="text"
|
||||
required
|
||||
defaultValue={editingZone?.name || ''}
|
||||
placeholder={__('e.g., Domestic, International, Europe')}
|
||||
className="w-full px-3 py-2 border rounded-md"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-2">
|
||||
{__('Regions')}
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground mb-3">
|
||||
{__('Select countries, states, or continents for this zone')}
|
||||
</p>
|
||||
<div className="border rounded-md max-h-[300px] overflow-y-auto">
|
||||
{availableLocations.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-muted-foreground">
|
||||
{__('Loading locations...')}
|
||||
</div>
|
||||
) : (
|
||||
<div className="divide-y">
|
||||
{availableLocations.map((location: any) => (
|
||||
<label
|
||||
key={location.code}
|
||||
className="flex items-center gap-3 p-3 hover:bg-accent cursor-pointer"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="regions"
|
||||
value={location.code}
|
||||
className="rounded"
|
||||
/>
|
||||
<span className="text-sm">{location.label}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-3 pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setShowAddZone(false);
|
||||
setEditingZone(null);
|
||||
}}
|
||||
>
|
||||
{__('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={createZoneMutation.isPending || updateZoneMutation.isPending}
|
||||
>
|
||||
{(createZoneMutation.isPending || updateZoneMutation.isPending) && (
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
)}
|
||||
{editingZone ? __('Update Zone') : __('Create Zone')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
</SettingsLayout>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user