fix(admin): set explicit width for product search dropdown in order form
Prevents the search dropdown from shrinking or overflowing unpredictably in the flex container. Also ensures better alignment.
This commit is contained in:
@@ -44,9 +44,9 @@ import { SearchableSelect } from '@/components/ui/searchable-select';
|
|||||||
export type CountryOption = { code: string; name: string };
|
export type CountryOption = { code: string; name: string };
|
||||||
export type StatesMap = Record<string, Record<string, string>>; // { US: { CA: 'California' } }
|
export type StatesMap = Record<string, Record<string, string>>; // { US: { CA: 'California' } }
|
||||||
export type PaymentChannel = { id: string; title: string; meta?: any };
|
export type PaymentChannel = { id: string; title: string; meta?: any };
|
||||||
export type PaymentMethod = {
|
export type PaymentMethod = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
channels?: PaymentChannel[]; // If present, show channels instead of gateway
|
channels?: PaymentChannel[]; // If present, show channels instead of gateway
|
||||||
};
|
};
|
||||||
@@ -113,7 +113,7 @@ type Props = {
|
|||||||
hideSubmitButton?: boolean;
|
hideSubmitButton?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const STATUS_LIST = ['pending','processing','on-hold','completed','cancelled','refunded','failed'];
|
const STATUS_LIST = ['pending', 'processing', 'on-hold', 'completed', 'cancelled', 'refunded', 'failed'];
|
||||||
|
|
||||||
// --- Component --------------------------------------------------------
|
// --- Component --------------------------------------------------------
|
||||||
export default function OrderForm({
|
export default function OrderForm({
|
||||||
@@ -167,11 +167,11 @@ export default function OrderForm({
|
|||||||
const only = countries[0]?.code || '';
|
const only = countries[0]?.code || '';
|
||||||
if (shipDiff) {
|
if (shipDiff) {
|
||||||
if (only && shippingData.country !== only) {
|
if (only && shippingData.country !== only) {
|
||||||
setShippingData({...shippingData, country: only});
|
setShippingData({ ...shippingData, country: only });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// keep shipping synced to billing when not different
|
// keep shipping synced to billing when not different
|
||||||
setShippingData({...shippingData, country: bCountry});
|
setShippingData({ ...shippingData, country: bCountry });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [oneCountryOnly, countries, shipDiff, bCountry, shippingData.country]);
|
}, [oneCountryOnly, countries, shipDiff, bCountry, shippingData.country]);
|
||||||
@@ -233,12 +233,12 @@ export default function OrderForm({
|
|||||||
|
|
||||||
// Debounce city input to avoid hitting backend on every keypress
|
// Debounce city input to avoid hitting backend on every keypress
|
||||||
const [debouncedCity, setDebouncedCity] = React.useState(effectiveShippingAddress.city);
|
const [debouncedCity, setDebouncedCity] = React.useState(effectiveShippingAddress.city);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
setDebouncedCity(effectiveShippingAddress.city);
|
setDebouncedCity(effectiveShippingAddress.city);
|
||||||
}, 500); // Wait 500ms after user stops typing
|
}, 500); // Wait 500ms after user stops typing
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [effectiveShippingAddress.city]);
|
}, [effectiveShippingAddress.city]);
|
||||||
|
|
||||||
@@ -295,10 +295,10 @@ export default function OrderForm({
|
|||||||
const products: ProductSearchItem[] = Array.isArray(raw)
|
const products: ProductSearchItem[] = Array.isArray(raw)
|
||||||
? raw
|
? raw
|
||||||
: Array.isArray(raw?.data)
|
: Array.isArray(raw?.data)
|
||||||
? raw.data
|
? raw.data
|
||||||
: Array.isArray(raw?.rows)
|
: Array.isArray(raw?.rows)
|
||||||
? raw.rows
|
? raw.rows
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const customersRaw = customersQ.data as any;
|
const customersRaw = customersQ.data as any;
|
||||||
const customers: any[] = Array.isArray(customersRaw) ? customersRaw : [];
|
const customers: any[] = Array.isArray(customersRaw) ? customersRaw : [];
|
||||||
@@ -311,7 +311,7 @@ export default function OrderForm({
|
|||||||
() => items.reduce((sum, it) => sum + (Number(it.qty) || 0) * (Number(it.price) || 0), 0),
|
() => items.reduce((sum, it) => sum + (Number(it.qty) || 0) * (Number(it.price) || 0), 0),
|
||||||
[items]
|
[items]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Calculate shipping cost
|
// Calculate shipping cost
|
||||||
// In edit mode: use existing order shipping total (fixed unless address changes)
|
// In edit mode: use existing order shipping total (fixed unless address changes)
|
||||||
// In create mode: calculate from selected shipping method
|
// In create mode: calculate from selected shipping method
|
||||||
@@ -325,34 +325,34 @@ export default function OrderForm({
|
|||||||
const method = shippings.find(s => s.id === shippingMethod);
|
const method = shippings.find(s => s.id === shippingMethod);
|
||||||
return method ? Number(method.cost) || 0 : 0;
|
return method ? Number(method.cost) || 0 : 0;
|
||||||
}, [mode, initial?.totals?.shipping, shippingMethod, shippings]);
|
}, [mode, initial?.totals?.shipping, shippingMethod, shippings]);
|
||||||
|
|
||||||
// Calculate discount from validated coupons
|
// Calculate discount from validated coupons
|
||||||
const couponDiscount = React.useMemo(() => {
|
const couponDiscount = React.useMemo(() => {
|
||||||
return validatedCoupons.reduce((sum, c) => sum + (c.discount_amount || 0), 0);
|
return validatedCoupons.reduce((sum, c) => sum + (c.discount_amount || 0), 0);
|
||||||
}, [validatedCoupons]);
|
}, [validatedCoupons]);
|
||||||
|
|
||||||
// Calculate order total (items + shipping - coupons)
|
// Calculate order total (items + shipping - coupons)
|
||||||
const orderTotal = React.useMemo(() => {
|
const orderTotal = React.useMemo(() => {
|
||||||
return Math.max(0, itemsTotal + shippingCost - couponDiscount);
|
return Math.max(0, itemsTotal + shippingCost - couponDiscount);
|
||||||
}, [itemsTotal, shippingCost, couponDiscount]);
|
}, [itemsTotal, shippingCost, couponDiscount]);
|
||||||
|
|
||||||
// Validate coupon
|
// Validate coupon
|
||||||
const validateCoupon = async (code: string) => {
|
const validateCoupon = async (code: string) => {
|
||||||
if (!code.trim()) return;
|
if (!code.trim()) return;
|
||||||
|
|
||||||
// Check if already added
|
// Check if already added
|
||||||
if (validatedCoupons.some(c => c.code.toLowerCase() === code.toLowerCase())) {
|
if (validatedCoupons.some(c => c.code.toLowerCase() === code.toLowerCase())) {
|
||||||
toast.error(__('Coupon already added'));
|
toast.error(__('Coupon already added'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCouponValidating(true);
|
setCouponValidating(true);
|
||||||
try {
|
try {
|
||||||
const response = await api.post('/coupons/validate', {
|
const response = await api.post('/coupons/validate', {
|
||||||
code: code.trim(),
|
code: code.trim(),
|
||||||
subtotal: itemsTotal,
|
subtotal: itemsTotal,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.valid) {
|
if (response.valid) {
|
||||||
setValidatedCoupons([...validatedCoupons, response]);
|
setValidatedCoupons([...validatedCoupons, response]);
|
||||||
setCouponInput('');
|
setCouponInput('');
|
||||||
@@ -366,7 +366,7 @@ export default function OrderForm({
|
|||||||
setCouponValidating(false);
|
setCouponValidating(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeCoupon = (code: string) => {
|
const removeCoupon = (code: string) => {
|
||||||
setValidatedCoupons(validatedCoupons.filter(c => c.code !== code));
|
setValidatedCoupons(validatedCoupons.filter(c => c.code !== code));
|
||||||
};
|
};
|
||||||
@@ -408,7 +408,7 @@ export default function OrderForm({
|
|||||||
|
|
||||||
// Keep shipping country synced to billing when unchecked
|
// Keep shipping country synced to billing when unchecked
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!shipDiff) setShippingData({...shippingData, country: bCountry});
|
if (!shipDiff) setShippingData({ ...shippingData, country: bCountry });
|
||||||
}, [shipDiff, bCountry]);
|
}, [shipDiff, bCountry]);
|
||||||
|
|
||||||
// Clamp states when country changes
|
// Clamp states when country changes
|
||||||
@@ -417,7 +417,7 @@ export default function OrderForm({
|
|||||||
}, [bCountry]);
|
}, [bCountry]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (shippingData.state && !states[shippingData.country]?.[shippingData.state]) {
|
if (shippingData.state && !states[shippingData.country]?.[shippingData.state]) {
|
||||||
setShippingData({...shippingData, state: ''});
|
setShippingData({ ...shippingData, state: '' });
|
||||||
}
|
}
|
||||||
}, [shippingData.country]);
|
}, [shippingData.country]);
|
||||||
|
|
||||||
@@ -426,7 +426,7 @@ export default function OrderForm({
|
|||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
async function handleSubmit(e: React.FormEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// For virtual-only products, don't send address fields
|
// For virtual-only products, don't send address fields
|
||||||
const billingData: any = {
|
const billingData: any = {
|
||||||
first_name: bFirst,
|
first_name: bFirst,
|
||||||
@@ -434,7 +434,7 @@ export default function OrderForm({
|
|||||||
email: bEmail,
|
email: bEmail,
|
||||||
phone: bPhone,
|
phone: bPhone,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only add address fields for physical products
|
// Only add address fields for physical products
|
||||||
if (hasPhysicalProduct) {
|
if (hasPhysicalProduct) {
|
||||||
billingData.address_1 = bAddr1;
|
billingData.address_1 = bAddr1;
|
||||||
@@ -443,7 +443,7 @@ export default function OrderForm({
|
|||||||
billingData.postcode = bPost;
|
billingData.postcode = bPost;
|
||||||
billingData.country = bCountry;
|
billingData.country = bCountry;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload: OrderPayload = {
|
const payload: OrderPayload = {
|
||||||
status,
|
status,
|
||||||
billing: billingData,
|
billing: billingData,
|
||||||
@@ -502,7 +502,7 @@ export default function OrderForm({
|
|||||||
onChange={(val: string) => {
|
onChange={(val: string) => {
|
||||||
const p = products.find((prod: ProductSearchItem) => String(prod.id) === val);
|
const p = products.find((prod: ProductSearchItem) => String(prod.id) === val);
|
||||||
if (!p) return;
|
if (!p) return;
|
||||||
|
|
||||||
// If variable product, show variation selector
|
// If variable product, show variation selector
|
||||||
if (p.type === 'variable' && p.variations && p.variations.length > 0) {
|
if (p.type === 'variable' && p.variations && p.variations.length > 0) {
|
||||||
setSelectedProduct(p);
|
setSelectedProduct(p);
|
||||||
@@ -510,7 +510,7 @@ export default function OrderForm({
|
|||||||
setShowVariationDrawer(true);
|
setShowVariationDrawer(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple product - add directly (but allow duplicates for different quantities)
|
// Simple product - add directly (but allow duplicates for different quantities)
|
||||||
setItems(prev => [
|
setItems(prev => [
|
||||||
...prev,
|
...prev,
|
||||||
@@ -530,6 +530,7 @@ export default function OrderForm({
|
|||||||
onSearch={setSearchQ}
|
onSearch={setSearchQ}
|
||||||
disabled={!itemsEditable}
|
disabled={!itemsEditable}
|
||||||
showCheckIndicator={false}
|
showCheckIndicator={false}
|
||||||
|
className="w-[200px] md:w-[300px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -733,7 +734,7 @@ export default function OrderForm({
|
|||||||
.map(([key, value]) => `${key}: ${value || ''}`)
|
.map(([key, value]) => `${key}: ${value || ''}`)
|
||||||
.filter(([_, value]) => value) // Remove empty values
|
.filter(([_, value]) => value) // Remove empty values
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={variation.id}
|
key={variation.id}
|
||||||
@@ -743,11 +744,11 @@ export default function OrderForm({
|
|||||||
const existingIndex = items.findIndex(
|
const existingIndex = items.findIndex(
|
||||||
item => item.product_id === selectedProduct.id && item.variation_id === variation.id
|
item => item.product_id === selectedProduct.id && item.variation_id === variation.id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
// Increment quantity of existing item
|
// Increment quantity of existing item
|
||||||
setItems(prev => prev.map((item, idx) =>
|
setItems(prev => prev.map((item, idx) =>
|
||||||
idx === existingIndex
|
idx === existingIndex
|
||||||
? { ...item, qty: item.qty + 1 }
|
? { ...item, qty: item.qty + 1 }
|
||||||
: item
|
: item
|
||||||
));
|
));
|
||||||
@@ -831,7 +832,7 @@ export default function OrderForm({
|
|||||||
.map(([key, value]) => `${key}: ${value || ''}`)
|
.map(([key, value]) => `${key}: ${value || ''}`)
|
||||||
.filter(([_, value]) => value) // Remove empty values
|
.filter(([_, value]) => value) // Remove empty values
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={variation.id}
|
key={variation.id}
|
||||||
@@ -841,11 +842,11 @@ export default function OrderForm({
|
|||||||
const existingIndex = items.findIndex(
|
const existingIndex = items.findIndex(
|
||||||
item => item.product_id === selectedProduct.id && item.variation_id === variation.id
|
item => item.product_id === selectedProduct.id && item.variation_id === variation.id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
// Increment quantity of existing item
|
// Increment quantity of existing item
|
||||||
setItems(prev => prev.map((item, idx) =>
|
setItems(prev => prev.map((item, idx) =>
|
||||||
idx === existingIndex
|
idx === existingIndex
|
||||||
? { ...item, qty: item.qty + 1 }
|
? { ...item, qty: item.qty + 1 }
|
||||||
: item
|
: item
|
||||||
));
|
));
|
||||||
@@ -924,7 +925,7 @@ export default function OrderForm({
|
|||||||
<span className="text-xs opacity-70">({__('locked')})</span>
|
<span className="text-xs opacity-70">({__('locked')})</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Coupon Input */}
|
{/* Coupon Input */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
@@ -949,7 +950,7 @@ export default function OrderForm({
|
|||||||
{couponValidating ? __('Validating...') : __('Apply')}
|
{couponValidating ? __('Validating...') : __('Apply')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Applied Coupons */}
|
{/* Applied Coupons */}
|
||||||
{validatedCoupons.length > 0 && (
|
{validatedCoupons.length > 0 && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -982,7 +983,7 @@ export default function OrderForm({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="text-[11px] opacity-70">
|
<div className="text-[11px] opacity-70">
|
||||||
{__('Enter coupon code and click Apply to validate and calculate discount')}
|
{__('Enter coupon code and click Apply to validate and calculate discount')}
|
||||||
</div>
|
</div>
|
||||||
@@ -1011,7 +1012,7 @@ export default function OrderForm({
|
|||||||
onChange={async (val: string) => {
|
onChange={async (val: string) => {
|
||||||
const customer = customers.find((c: any) => String(c.id) === val);
|
const customer = customers.find((c: any) => String(c.id) === val);
|
||||||
if (!customer) return;
|
if (!customer) return;
|
||||||
|
|
||||||
// Fetch full customer data
|
// Fetch full customer data
|
||||||
try {
|
try {
|
||||||
const data = await CustomersApi.searchByEmail(customer.email);
|
const data = await CustomersApi.searchByEmail(customer.email);
|
||||||
@@ -1021,7 +1022,7 @@ export default function OrderForm({
|
|||||||
setBLast(data.billing.last_name || data.last_name || '');
|
setBLast(data.billing.last_name || data.last_name || '');
|
||||||
setBEmail(data.email || '');
|
setBEmail(data.email || '');
|
||||||
setBPhone(data.billing.phone || '');
|
setBPhone(data.billing.phone || '');
|
||||||
|
|
||||||
// Only fill address fields if cart has physical products
|
// Only fill address fields if cart has physical products
|
||||||
if (hasPhysicalProduct) {
|
if (hasPhysicalProduct) {
|
||||||
setBAddr1(data.billing.address_1 || '');
|
setBAddr1(data.billing.address_1 || '');
|
||||||
@@ -1029,7 +1030,7 @@ export default function OrderForm({
|
|||||||
setBPost(data.billing.postcode || '');
|
setBPost(data.billing.postcode || '');
|
||||||
setBCountry(data.billing.country || bCountry);
|
setBCountry(data.billing.country || bCountry);
|
||||||
setBState(data.billing.state || '');
|
setBState(data.billing.state || '');
|
||||||
|
|
||||||
// Autofill shipping if available
|
// Autofill shipping if available
|
||||||
if (data.shipping && data.shipping.address_1) {
|
if (data.shipping && data.shipping.address_1) {
|
||||||
setShipDiff(true);
|
setShipDiff(true);
|
||||||
@@ -1044,14 +1045,14 @@ export default function OrderForm({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark customer as selected
|
// Mark customer as selected
|
||||||
setSelectedCustomerId(data.user_id);
|
setSelectedCustomerId(data.user_id);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Customer autofill error:', e);
|
console.error('Customer autofill error:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCustomerSearchQ('');
|
setCustomerSearchQ('');
|
||||||
}}
|
}}
|
||||||
onSearch={setCustomerSearchQ}
|
onSearch={setCustomerSearchQ}
|
||||||
@@ -1063,40 +1064,40 @@ export default function OrderForm({
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<Label>{__('First name')}</Label>
|
<Label>{__('First name')}</Label>
|
||||||
<Input className="rounded-md border px-3 py-2" value={bFirst} onChange={e=>setBFirst(e.target.value)} />
|
<Input className="rounded-md border px-3 py-2" value={bFirst} onChange={e => setBFirst(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>{__('Last name')}</Label>
|
<Label>{__('Last name')}</Label>
|
||||||
<Input className="rounded-md border px-3 py-2" value={bLast} onChange={e=>setBLast(e.target.value)} />
|
<Input className="rounded-md border px-3 py-2" value={bLast} onChange={e => setBLast(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>{__('Email')}</Label>
|
<Label>{__('Email')}</Label>
|
||||||
<Input
|
<Input
|
||||||
inputMode="email"
|
inputMode="email"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
className="rounded-md border px-3 py-2 appearance-none"
|
className="rounded-md border px-3 py-2 appearance-none"
|
||||||
value={bEmail}
|
value={bEmail}
|
||||||
onChange={e=>setBEmail(e.target.value)}
|
onChange={e => setBEmail(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>{__('Phone')}</Label>
|
<Label>{__('Phone')}</Label>
|
||||||
<Input className="rounded-md border px-3 py-2" value={bPhone} onChange={e=>setBPhone(e.target.value)} />
|
<Input className="rounded-md border px-3 py-2" value={bPhone} onChange={e => setBPhone(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
{/* Only show full address fields for physical products */}
|
{/* Only show full address fields for physical products */}
|
||||||
{hasPhysicalProduct && (
|
{hasPhysicalProduct && (
|
||||||
<>
|
<>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<Label>{__('Address')}</Label>
|
<Label>{__('Address')}</Label>
|
||||||
<Input className="rounded-md border px-3 py-2" value={bAddr1} onChange={e=>setBAddr1(e.target.value)} />
|
<Input className="rounded-md border px-3 py-2" value={bAddr1} onChange={e => setBAddr1(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>{__('City')}</Label>
|
<Label>{__('City')}</Label>
|
||||||
<Input className="rounded-md border px-3 py-2" value={bCity} onChange={e=>setBCity(e.target.value)} />
|
<Input className="rounded-md border px-3 py-2" value={bCity} onChange={e => setBCity(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>{__('Postcode')}</Label>
|
<Label>{__('Postcode')}</Label>
|
||||||
<Input className="rounded-md border px-3 py-2" value={bPost} onChange={e=>setBPost(e.target.value)} />
|
<Input className="rounded-md border px-3 py-2" value={bPost} onChange={e => setBPost(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>{__('Country')}</Label>
|
<Label>{__('Country')}</Label>
|
||||||
@@ -1137,7 +1138,7 @@ export default function OrderForm({
|
|||||||
{hasPhysicalProduct && (
|
{hasPhysicalProduct && (
|
||||||
<div className="pt-2 mt-4">
|
<div className="pt-2 mt-4">
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-2 text-sm">
|
||||||
<Checkbox id="shipDiff" checked={shipDiff} onCheckedChange={(v)=> setShipDiff(Boolean(v))} />
|
<Checkbox id="shipDiff" checked={shipDiff} onCheckedChange={(v) => setShipDiff(Boolean(v))} />
|
||||||
<Label htmlFor="shipDiff" className="leading-none">{__('Ship to a different address')}</Label>
|
<Label htmlFor="shipDiff" className="leading-none">{__('Ship to a different address')}</Label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1154,7 +1155,7 @@ export default function OrderForm({
|
|||||||
.map((field: any) => {
|
.map((field: any) => {
|
||||||
const isWide = ['address_1', 'address_2'].includes(field.key.replace('shipping_', ''));
|
const isWide = ['address_1', 'address_2'].includes(field.key.replace('shipping_', ''));
|
||||||
const fieldKey = field.key.replace('shipping_', '');
|
const fieldKey = field.key.replace('shipping_', '');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={field.key} className={isWide ? 'md:col-span-2' : ''}>
|
<div key={field.key} className={isWide ? 'md:col-span-2' : ''}>
|
||||||
<Label>
|
<Label>
|
||||||
@@ -1162,9 +1163,9 @@ export default function OrderForm({
|
|||||||
{field.required && <span className="text-destructive ml-1">*</span>}
|
{field.required && <span className="text-destructive ml-1">*</span>}
|
||||||
</Label>
|
</Label>
|
||||||
{field.type === 'select' && field.options ? (
|
{field.type === 'select' && field.options ? (
|
||||||
<Select
|
<Select
|
||||||
value={shippingData[fieldKey] || ''}
|
value={shippingData[fieldKey] || ''}
|
||||||
onValueChange={(v) => setShippingData({...shippingData, [fieldKey]: v})}
|
onValueChange={(v) => setShippingData({ ...shippingData, [fieldKey]: v })}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
<SelectValue placeholder={field.placeholder || field.label} />
|
<SelectValue placeholder={field.placeholder || field.label} />
|
||||||
@@ -1179,14 +1180,14 @@ export default function OrderForm({
|
|||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
options={countryOptions}
|
options={countryOptions}
|
||||||
value={shippingData.country || ''}
|
value={shippingData.country || ''}
|
||||||
onChange={(v) => setShippingData({...shippingData, country: v})}
|
onChange={(v) => setShippingData({ ...shippingData, country: v })}
|
||||||
placeholder={field.placeholder || __('Select country')}
|
placeholder={field.placeholder || __('Select country')}
|
||||||
disabled={oneCountryOnly}
|
disabled={oneCountryOnly}
|
||||||
/>
|
/>
|
||||||
) : field.type === 'textarea' ? (
|
) : field.type === 'textarea' ? (
|
||||||
<Textarea
|
<Textarea
|
||||||
value={shippingData[fieldKey] || ''}
|
value={shippingData[fieldKey] || ''}
|
||||||
onChange={(e) => setShippingData({...shippingData, [fieldKey]: e.target.value})}
|
onChange={(e) => setShippingData({ ...shippingData, [fieldKey]: e.target.value })}
|
||||||
placeholder={field.placeholder}
|
placeholder={field.placeholder}
|
||||||
required={field.required}
|
required={field.required}
|
||||||
/>
|
/>
|
||||||
@@ -1194,7 +1195,7 @@ export default function OrderForm({
|
|||||||
<Input
|
<Input
|
||||||
type={field.type === 'email' ? 'email' : field.type === 'tel' ? 'tel' : 'text'}
|
type={field.type === 'email' ? 'email' : field.type === 'tel' ? 'tel' : 'text'}
|
||||||
value={shippingData[fieldKey] || ''}
|
value={shippingData[fieldKey] || ''}
|
||||||
onChange={(e) => setShippingData({...shippingData, [fieldKey]: e.target.value})}
|
onChange={(e) => setShippingData({ ...shippingData, [fieldKey]: e.target.value })}
|
||||||
placeholder={field.placeholder}
|
placeholder={field.placeholder}
|
||||||
required={field.required}
|
required={field.required}
|
||||||
/>
|
/>
|
||||||
@@ -1281,7 +1282,7 @@ export default function OrderForm({
|
|||||||
|
|
||||||
<div className="rounded border p-4 space-y-2">
|
<div className="rounded border p-4 space-y-2">
|
||||||
<Label>{__('Customer note (optional)')}</Label>
|
<Label>{__('Customer note (optional)')}</Label>
|
||||||
<Textarea value={note} onChange={e=>setNote(e.target.value)} placeholder={__('Write a note for this order…')} />
|
<Textarea value={note} onChange={e => setNote(e.target.value)} placeholder={__('Write a note for this order…')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!hideSubmitButton && (
|
{!hideSubmitButton && (
|
||||||
@@ -1297,6 +1298,6 @@ export default function OrderForm({
|
|||||||
|
|
||||||
function isEmptyAddress(a: any) {
|
function isEmptyAddress(a: any) {
|
||||||
if (!a) return true;
|
if (!a) return true;
|
||||||
const keys = ['first_name','last_name','address_1','city','state','postcode','country'];
|
const keys = ['first_name', 'last_name', 'address_1', 'city', 'state', 'postcode', 'country'];
|
||||||
return keys.every(k => !a[k]);
|
return keys.every(k => !a[k]);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user