feat(F-069): completed feature
This commit is contained in:
@@ -76,7 +76,12 @@ function MethodRow({ method, onEdit, onDelete }: { method: ShippingMethod; onEdi
|
||||
const fmt = (cents: number) => `€${(cents / 100).toFixed(2)}`;
|
||||
return (
|
||||
<tr className="hover:bg-gray-50 transition-colors">
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">{method.name}</td>
|
||||
<td className="px-6 py-4">
|
||||
<p className="text-sm font-medium text-gray-900">{method.name}</p>
|
||||
{method.description && (
|
||||
<p className="text-xs text-gray-500 mt-0.5 max-w-xs">{method.description}</p>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">{method.zoneName}</td>
|
||||
<td className="px-6 py-4 text-sm font-semibold text-gray-800">{fmt(method.baseCostCents)}</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-500">{method.freeShippingThresholdCents ? `Gratis desde ${fmt(method.freeShippingThresholdCents)}` : '—'}</td>
|
||||
@@ -97,6 +102,7 @@ function MethodForm({ zones, method, onSave, onCancel }: { zones: ShippingZone[]
|
||||
const [zoneId, setZoneId] = useState(method?.zoneId ?? zones[0]?.id ?? '');
|
||||
const [cost, setCost] = useState(method ? String(method.baseCostCents / 100) : '');
|
||||
const [threshold, setThreshold] = useState(method?.freeShippingThresholdCents ? String(method.freeShippingThresholdCents / 100) : '');
|
||||
const [description, setDescription] = useState(method?.description ?? '');
|
||||
const [active, setActive] = useState(method?.active ?? true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [err, setErr] = useState('');
|
||||
@@ -106,10 +112,24 @@ function MethodForm({ zones, method, onSave, onCancel }: { zones: ShippingZone[]
|
||||
try {
|
||||
const baseCostCents = Math.round(parseFloat(cost) * 100);
|
||||
const freeThreshold = threshold ? Math.round(parseFloat(threshold) * 100) : null;
|
||||
const descriptionPayload = description.trim() || null;
|
||||
if (method) {
|
||||
await shippingApi.updateMethod(method.id, { name, baseCostCents, freeShippingThresholdCents: freeThreshold, active });
|
||||
await shippingApi.updateMethod(method.id, {
|
||||
name,
|
||||
baseCostCents,
|
||||
freeShippingThresholdCents: freeThreshold,
|
||||
description: descriptionPayload,
|
||||
active,
|
||||
});
|
||||
} else {
|
||||
await shippingApi.createMethod({ zoneId, name, baseCostCents, freeShippingThresholdCents: freeThreshold, active });
|
||||
await shippingApi.createMethod({
|
||||
zoneId,
|
||||
name,
|
||||
baseCostCents,
|
||||
freeShippingThresholdCents: freeThreshold,
|
||||
description: descriptionPayload,
|
||||
active,
|
||||
});
|
||||
}
|
||||
onSave(); onCancel();
|
||||
} catch (er) { setErr(er instanceof Error ? er.message : 'Error'); } finally { setSaving(false); }
|
||||
@@ -117,8 +137,12 @@ function MethodForm({ zones, method, onSave, onCancel }: { zones: ShippingZone[]
|
||||
|
||||
return (
|
||||
<tr className="bg-green-50/50 border-b border-green-100">
|
||||
<td className="px-4 py-3"><input value={name} onChange={e => setName(e.target.value)} required placeholder="Nombre método"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-[#2D6A4F] outline-none" /></td>
|
||||
<td className="px-4 py-3">
|
||||
<input value={name} onChange={e => setName(e.target.value)} required placeholder="Nombre método"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-[#2D6A4F] outline-none" />
|
||||
<input value={description} onChange={e => setDescription(e.target.value)} placeholder="Descripción para el checkout"
|
||||
className="w-full px-3 py-2 mt-1.5 border border-gray-300 rounded-lg text-xs focus:ring-2 focus:ring-[#2D6A4F] outline-none" />
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<select value={zoneId} onChange={e => setZoneId(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-[#2D6A4F] outline-none">
|
||||
|
||||
@@ -271,7 +271,7 @@ export interface ShippingZone {
|
||||
}
|
||||
export interface ShippingMethod {
|
||||
id: string; zoneId: string; zoneName: string; name: string;
|
||||
baseCostCents: number; freeShippingThresholdCents: number | null; active: boolean;
|
||||
baseCostCents: number; freeShippingThresholdCents: number | null; description: string | null; active: boolean;
|
||||
}
|
||||
export const shippingApi = {
|
||||
listZones: () => api.get<{ items: ShippingZone[] }>('/api/admin/shipping/zones'),
|
||||
@@ -281,9 +281,9 @@ export const shippingApi = {
|
||||
api.patch('/api/admin/shipping/zones/' + id, data),
|
||||
deleteZone: (id: string) => api.delete<void>('/api/admin/shipping/zones/' + id),
|
||||
listMethods: () => api.get<{ items: ShippingMethod[] }>('/api/admin/shipping/methods'),
|
||||
createMethod: (data: { zoneId: string; name: string; baseCostCents: number; freeShippingThresholdCents?: number | null; active?: boolean }) =>
|
||||
createMethod: (data: { zoneId: string; name: string; baseCostCents: number; freeShippingThresholdCents?: number | null; description?: string | null; active?: boolean }) =>
|
||||
api.post<{ id: string }>('/api/admin/shipping/methods', data),
|
||||
updateMethod: (id: string, data: Partial<{ name: string; baseCostCents: number; freeShippingThresholdCents?: number | null; active: boolean }>) =>
|
||||
updateMethod: (id: string, data: Partial<{ name: string; baseCostCents: number; freeShippingThresholdCents?: number | null; description?: string | null; active: boolean }>) =>
|
||||
api.patch('/api/admin/shipping/methods/' + id, data),
|
||||
deleteMethod: (id: string) => api.delete<void>('/api/admin/shipping/methods/' + id),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user