feat: Implement live Shipping settings page
Implemented functional Shipping settings page with WooCommerce integration. Features: ✅ Fetch shipping zones from WooCommerce API ✅ Display zones with rates in card layout ✅ Refresh button to reload data ✅ "View in WooCommerce" button for full settings ✅ Edit zone links to WooCommerce ✅ Add zone link to WooCommerce ✅ Loading states with spinner ✅ Empty state when no zones configured ✅ Internationalization (i18n) throughout ✅ Shipping tips help card Implementation: - Uses React Query for data fetching - Integrates with WooCommerce shipping API - Links to WooCommerce for detailed configuration - Clean, modern UI matching Payments page - Responsive design API Endpoint: - GET /settings/shipping/zones Note: Full CRUD operations handled in WooCommerce for now. Future: Add inline editing capabilities.
This commit is contained in:
@@ -130,4 +130,9 @@
|
||||
|
||||
/* --- WooNooW: Popper menus & fullscreen fixes --- */
|
||||
[data-radix-popper-content-wrapper] { z-index: 2147483647 !important; }
|
||||
body.woonoow-fullscreen .woonoow-app { overflow: visible; }
|
||||
body.woonoow-fullscreen .woonoow-app { overflow: visible; }
|
||||
|
||||
/* a[href] {
|
||||
color: rgb(34 197 94);
|
||||
font-weight: bold;
|
||||
} */
|
||||
@@ -1,10 +1,13 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from '@/lib/api';
|
||||
import { SettingsLayout } from './components/SettingsLayout';
|
||||
import { SettingsCard } from './components/SettingsCard';
|
||||
import { ToggleField } from './components/ToggleField';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Globe, Truck, MapPin, Edit, Trash2 } from 'lucide-react';
|
||||
import { Globe, Truck, MapPin, Edit, Trash2, RefreshCw, Loader2, ExternalLink } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { __ } from '@/lib/i18n';
|
||||
|
||||
interface ShippingRate {
|
||||
id: string;
|
||||
@@ -22,68 +25,79 @@ interface ShippingZone {
|
||||
}
|
||||
|
||||
export default function ShippingPage() {
|
||||
const [zones] = useState<ShippingZone[]>([
|
||||
{
|
||||
id: 'domestic',
|
||||
name: 'Domestic (Indonesia)',
|
||||
regions: 'All Indonesia',
|
||||
rates: [
|
||||
{
|
||||
id: 'standard',
|
||||
name: 'Standard Shipping',
|
||||
price: 'Rp 15,000',
|
||||
transitTime: '3-5 business days',
|
||||
},
|
||||
{
|
||||
id: 'express',
|
||||
name: 'Express Shipping',
|
||||
price: 'Rp 30,000',
|
||||
transitTime: '1-2 business days',
|
||||
},
|
||||
{
|
||||
id: 'free',
|
||||
name: 'Free Shipping',
|
||||
price: 'Free',
|
||||
condition: 'Order total > Rp 500,000',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'international',
|
||||
name: 'International',
|
||||
regions: 'Rest of world',
|
||||
rates: [
|
||||
{
|
||||
id: 'intl',
|
||||
name: 'International Shipping',
|
||||
price: 'Calculated',
|
||||
transitTime: '7-14 business days',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const queryClient = useQueryClient();
|
||||
const wcAdminUrl = (window as any).WNW_CONFIG?.wpAdminUrl || '/wp-admin';
|
||||
|
||||
// Fetch shipping zones from WooCommerce
|
||||
const { data: zones = [], isLoading, refetch } = useQuery({
|
||||
queryKey: ['shipping-zones'],
|
||||
queryFn: () => api.get('/settings/shipping/zones'),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
});
|
||||
|
||||
const [localPickup, setLocalPickup] = useState(true);
|
||||
const [showDeliveryEstimates, setShowDeliveryEstimates] = useState(true);
|
||||
|
||||
const handleSave = async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
toast.success('Shipping settings have been updated successfully.');
|
||||
};
|
||||
if (isLoading) {
|
||||
return (
|
||||
<SettingsLayout
|
||||
title={__('Shipping & Delivery')}
|
||||
description={__('Manage how you ship products to customers')}
|
||||
>
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
</SettingsLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsLayout
|
||||
title="Shipping & Delivery"
|
||||
description="Manage how you ship products to customers"
|
||||
onSave={handleSave}
|
||||
title={__('Shipping & Delivery')}
|
||||
description={__('Manage how you ship products to customers')}
|
||||
action={
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => refetch()}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<RefreshCw className="h-4 w-4 mr-2" />
|
||||
{__('Refresh')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
asChild
|
||||
>
|
||||
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping`} target="_blank" rel="noopener noreferrer">
|
||||
<ExternalLink className="h-4 w-4 mr-2" />
|
||||
{__('View in WooCommerce')}
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{/* Shipping Zones */}
|
||||
<SettingsCard
|
||||
title="Shipping Zones"
|
||||
description="Create zones to group regions with similar shipping rates"
|
||||
title={__('Shipping Zones')}
|
||||
description={__('Create zones to group regions with similar shipping rates')}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{zones.map((zone) => (
|
||||
{zones.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
{__('No shipping zones configured yet.')}
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
asChild
|
||||
>
|
||||
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping`} target="_blank" rel="noopener noreferrer">
|
||||
{__('Configure in WooCommerce')}
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{zones.map((zone: any) => (
|
||||
<div
|
||||
key={zone.id}
|
||||
className="border rounded-lg p-4 hover:border-primary/50 transition-colors"
|
||||
@@ -104,21 +118,22 @@ export default function ShippingPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline" size="sm">
|
||||
<Edit className="h-4 w-4 mr-2" />
|
||||
Edit zone
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
asChild
|
||||
>
|
||||
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping&zone_id=${zone.id}`} target="_blank" rel="noopener noreferrer">
|
||||
<Edit className="h-4 w-4 mr-2" />
|
||||
{__('Edit zone')}
|
||||
</a>
|
||||
</Button>
|
||||
{zone.id !== 'domestic' && (
|
||||
<Button variant="ghost" size="sm">
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Shipping Rates */}
|
||||
<div className="pl-11 space-y-2">
|
||||
{zone.rates.map((rate) => (
|
||||
{zone.rates?.map((rate: any) => (
|
||||
<div
|
||||
key={rate.id}
|
||||
className="flex items-center justify-between py-2 px-3 bg-muted/50 rounded-md"
|
||||
@@ -144,88 +159,29 @@ export default function ShippingPage() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
|
||||
<Button variant="outline" className="w-full">
|
||||
+ Add shipping zone
|
||||
</Button>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
|
||||
{/* Local Pickup */}
|
||||
<SettingsCard
|
||||
title="Local Pickup"
|
||||
description="Let customers pick up orders from your location"
|
||||
>
|
||||
<ToggleField
|
||||
id="localPickup"
|
||||
label="Enable local pickup"
|
||||
description="Customers can choose to pick up their order instead of shipping"
|
||||
checked={localPickup}
|
||||
onCheckedChange={setLocalPickup}
|
||||
/>
|
||||
|
||||
{localPickup && (
|
||||
<div className="mt-4 p-4 border rounded-lg space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<MapPin className="h-5 w-5 text-primary mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<p className="font-medium">Main Store</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Jl. Example No. 123, Jakarta 12345
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Mon-Fri: 9:00 AM - 5:00 PM
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" className="w-full">
|
||||
+ Add pickup location
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
asChild
|
||||
>
|
||||
<a href={`${wcAdminUrl}/admin.php?page=wc-settings&tab=shipping&action=add_zone`} target="_blank" rel="noopener noreferrer">
|
||||
+ {__('Add shipping zone')}
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</SettingsCard>
|
||||
|
||||
{/* Shipping Options */}
|
||||
<SettingsCard
|
||||
title="Shipping Options"
|
||||
description="Additional shipping settings"
|
||||
>
|
||||
<ToggleField
|
||||
id="deliveryEstimates"
|
||||
label="Show delivery estimates"
|
||||
description="Display estimated delivery dates at checkout"
|
||||
checked={showDeliveryEstimates}
|
||||
onCheckedChange={setShowDeliveryEstimates}
|
||||
/>
|
||||
|
||||
<ToggleField
|
||||
id="hideCosts"
|
||||
label="Hide shipping costs until address entered"
|
||||
description="Require customers to enter their address before showing shipping costs"
|
||||
checked={false}
|
||||
onCheckedChange={() => { /* TODO: Implement */ }}
|
||||
/>
|
||||
|
||||
<ToggleField
|
||||
id="requireAddress"
|
||||
label="Require shipping address"
|
||||
description="Always collect shipping address, even for digital products"
|
||||
checked={false}
|
||||
onCheckedChange={() => { /* TODO: Implement */ }}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
{/* Help Card */}
|
||||
<div className="bg-muted/50 border rounded-lg p-4">
|
||||
<p className="text-sm font-medium mb-2">💡 Shipping tips</p>
|
||||
<p className="text-sm font-medium mb-2">💡 {__('Shipping tips')}</p>
|
||||
<ul className="text-sm text-muted-foreground space-y-1">
|
||||
<li>• Offer free shipping for orders above a certain amount to increase average order value</li>
|
||||
<li>• Provide multiple shipping options to give customers flexibility</li>
|
||||
<li>• Set realistic delivery estimates to manage customer expectations</li>
|
||||
<li>• {__('Offer free shipping for orders above a certain amount to increase average order value')}</li>
|
||||
<li>• {__('Provide multiple shipping options to give customers flexibility')}</li>
|
||||
<li>• {__('Set realistic delivery estimates to manage customer expectations')}</li>
|
||||
<li>• {__('Configure detailed shipping settings in WooCommerce for full control')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</SettingsLayout>
|
||||
|
||||
Reference in New Issue
Block a user