feat(manager): add Digital Menu tab to ProductFormModal
Adds a new "Digital Menu" tab with three sections: - Visibility: digital_visible / digital_available toggles - Display overrides: digital_name, digital_description, digital_image_url - Pricing: digital_price / digital_discount with live customer-price preview All 7 fields are wired into form state (buildFormFromProduct) and included in the PUT request body (buildBody). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -139,6 +139,13 @@ function buildFormFromProduct(product) {
|
||||
} : null,
|
||||
is_favorite: ps.is_favorite ?? false, favorite_sort_order: ps.favorite_sort_order ?? 0,
|
||||
})) ?? [],
|
||||
digital_visible: product.digital_visible ?? true,
|
||||
digital_available: product.digital_available ?? true,
|
||||
digital_name: product.digital_name ?? '',
|
||||
digital_description: product.digital_description ?? '',
|
||||
digital_image_url: product.digital_image_url ?? '',
|
||||
digital_price: product.digital_price ?? '',
|
||||
digital_discount: product.digital_discount ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,6 +432,13 @@ export default function ProductFormModal({ product, categories, printers, onSave
|
||||
})),
|
||||
is_favorite: ps.is_favorite ?? false, favorite_sort_order: ps.favorite_sort_order ?? 0,
|
||||
})),
|
||||
digital_visible: form.digital_visible,
|
||||
digital_available: form.digital_available,
|
||||
digital_name: form.digital_name || null,
|
||||
digital_description: form.digital_description || null,
|
||||
digital_image_url: form.digital_image_url || null,
|
||||
digital_price: form.digital_price !== '' ? parseFloat(form.digital_price) : null,
|
||||
digital_discount: form.digital_discount !== '' ? parseFloat(form.digital_discount) : null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,6 +477,7 @@ export default function ProductFormModal({ product, categories, printers, onSave
|
||||
{ key: 'options', label: 'Έξτρα', count: form.options.length },
|
||||
...form.preference_sets.map((ps, i) => ({ key: i, label: ps.name || `Προτ. ${i + 1}`, count: ps.choices.length })),
|
||||
{ key: '__add_pref__', label: '+ Προτίμηση', isAdd: true },
|
||||
{ key: 'digital_menu', label: 'Digital Menu' },
|
||||
]
|
||||
const favList = buildFavoritesList(form)
|
||||
|
||||
@@ -699,6 +714,94 @@ export default function ProductFormModal({ product, categories, printers, onSave
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Digital Menu tab */}
|
||||
{activeTab === 'digital_menu' && (() => {
|
||||
const basePrice = parseFloat(form.base_price) || 0
|
||||
const dp = form.digital_price !== '' ? parseFloat(form.digital_price) : null
|
||||
const disc = form.digital_discount !== '' ? parseFloat(form.digital_discount) : null
|
||||
let customerPrice = basePrice
|
||||
if (dp !== null && !isNaN(dp)) customerPrice = dp
|
||||
else if (disc !== null && !isNaN(disc) && disc > 0) customerPrice = Math.round(basePrice * (1 - disc / 100) * 100) / 100
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Section 1 — Visibility */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-gray-400 uppercase tracking-widest mb-3">Ορατότητα</p>
|
||||
<div className="space-y-3">
|
||||
<label className="flex items-center gap-3 cursor-pointer select-none">
|
||||
<input type="checkbox" checked={form.digital_visible}
|
||||
onChange={e => setField('digital_visible', e.target.checked)}
|
||||
className="w-4 h-4 accent-primary-700" />
|
||||
<span className="text-sm font-medium text-gray-700">Show on digital menu</span>
|
||||
</label>
|
||||
<div>
|
||||
<label className="flex items-center gap-3 cursor-pointer select-none">
|
||||
<input type="checkbox" checked={form.digital_available}
|
||||
onChange={e => setField('digital_available', e.target.checked)}
|
||||
className="w-4 h-4 accent-primary-700" />
|
||||
<span className="text-sm font-medium text-gray-700">Available for ordering</span>
|
||||
</label>
|
||||
<p className="text-xs text-gray-400 mt-1 ml-7">Uncheck to show as ‘Out of stock’</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section 2 — Display overrides */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-gray-400 uppercase tracking-widest mb-1">Display overrides</p>
|
||||
<p className="text-xs text-gray-400 mb-3">Leave blank to use the standard product value</p>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="label">Display name (override)</label>
|
||||
<input className="input" value={form.digital_name}
|
||||
onChange={e => setField('digital_name', e.target.value)}
|
||||
placeholder={form.name || 'Leave blank to use product name'} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="label">Description</label>
|
||||
<textarea className="input resize-none" rows={3}
|
||||
value={form.digital_description}
|
||||
onChange={e => setField('digital_description', e.target.value)}
|
||||
placeholder="Leave blank to use product description"
|
||||
style={{ lineHeight: 1.5, fontSize: 13 }} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="label">Image URL</label>
|
||||
<input className="input" value={form.digital_image_url}
|
||||
onChange={e => setField('digital_image_url', e.target.value)}
|
||||
placeholder="https://…" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section 3 — Pricing */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-gray-400 uppercase tracking-widest mb-3">Pricing</p>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="label">Online price (override)</label>
|
||||
<input type="number" step="0.01" min="0" className="input w-40"
|
||||
value={form.digital_price}
|
||||
onChange={e => setField('digital_price', e.target.value)}
|
||||
placeholder="e.g. 9.50" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="label">Discount %</label>
|
||||
<input type="number" step="1" min="0" max="100" className="input w-40"
|
||||
value={form.digital_discount}
|
||||
onChange={e => setField('digital_discount', e.target.value)}
|
||||
placeholder="e.g. 10 for 10% off" />
|
||||
</div>
|
||||
<div className="rounded-lg bg-green-50 border border-green-200 px-4 py-3 text-sm text-green-800">
|
||||
Customer sees: <span className="font-bold">€{customerPrice.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
{/* Preference set tab */}
|
||||
{typeof activeTab === 'number' && form.preference_sets[activeTab] && (() => {
|
||||
const si = activeTab
|
||||
|
||||
Reference in New Issue
Block a user