Feature: Order tracking with IDs, 30% deposit calculation, PayID support, and post-submission instructions
This commit is contained in:
1 parent
108184ba68
commit
fd1d7c0025
12 files changed
+621
-116
No files matched your search
+10
-7
@@ -16,6 +16,7 @@ function App() {
|
||||
const [adminCodeInput, setAdminCodeInput] = useState('');
|
||||
const [notification, setNotification] = useState<string | null>(null);
|
||||
const [adminCode, setAdminCode] = useState('1234');
|
||||
const [config, setConfig] = useState<any>(null);
|
||||
const [confirmModal, setConfirmModal] = useState<{ show: boolean, message: string, onConfirm: (() => void) | null }>({ show: false, message: '', onConfirm: null });
|
||||
|
||||
useEffect(() => {
|
||||
@@ -29,8 +30,9 @@ function App() {
|
||||
const data = await API.fetchData();
|
||||
setStock(data.stock);
|
||||
setOrders(data.orders.sort((a: any, b: any) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()));
|
||||
if (data.config && data.config.adminCode) {
|
||||
setAdminCode(data.config.adminCode);
|
||||
if (data.config) {
|
||||
setAdminCode(data.config.adminCode || '1234');
|
||||
setConfig(data.config);
|
||||
}
|
||||
setIsLoading(false);
|
||||
} catch (err) {
|
||||
@@ -101,13 +103,14 @@ function App() {
|
||||
};
|
||||
|
||||
try {
|
||||
await API.submitOrder(orderData);
|
||||
const order = await API.submitOrder(orderData);
|
||||
setCart([]);
|
||||
setShowCartModal(false);
|
||||
refreshData();
|
||||
showNotification("Order Request Sent! We'll contact you.");
|
||||
showNotification("Order Request Sent!");
|
||||
return order;
|
||||
} catch (error) {
|
||||
showNotification("Error submitting order.");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -155,10 +158,10 @@ function App() {
|
||||
|
||||
<main className="max-w-6xl mx-auto px-4 py-8">
|
||||
{notification && <div className="fixed bottom-4 right-4 bg-red-600 text-white px-6 py-3 rounded-lg shadow-2xl animate-bounce z-50 font-bold tracking-wide border border-red-400">{notification}</div>}
|
||||
{view === 'customer' ? <CustomerView stock={stock} addToCart={addToCart} /> : <AdminDashboard stock={stock} orders={orders} refreshData={refreshData} notify={showNotification} promptConfirm={promptConfirm} />}
|
||||
{view === 'customer' ? <CustomerView stock={stock} addToCart={addToCart} config={config} /> : <AdminDashboard stock={stock} orders={orders} refreshData={refreshData} notify={showNotification} promptConfirm={promptConfirm} />}
|
||||
</main>
|
||||
|
||||
{showCartModal && <CartModal cart={cart} onClose={() => setShowCartModal(false)} onRemove={removeFromCart} onUpdateQty={updateCartQty} onSubmit={submitOrder} />}
|
||||
{showCartModal && <CartModal cart={cart} config={config} onClose={() => setShowCartModal(false)} onRemove={removeFromCart} onUpdateQty={updateCartQty} onSubmit={submitOrder} />}
|
||||
|
||||
{showAdminLogin && (
|
||||
<div className="fixed inset-0 bg-black/90 backdrop-blur-sm flex items-center justify-center p-4 z-50">
|
||||
|
||||
@@ -130,25 +130,45 @@ export default function AdminDashboard({ stock, orders, refreshData, notify, pro
|
||||
<div key={order.id} className="bg-stone-950 p-5 rounded-lg border border-stone-800 relative group hover:border-stone-700 transition-colors shadow-md">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h4 className="font-bold text-lg text-red-400">{order.name}</h4>
|
||||
<p className="text-sm text-stone-300 font-mono mb-1">{order.phone}</p>
|
||||
<p className="text-xs text-stone-500">{new Date(order.timestamp).toLocaleString()}</p>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-[10px] font-black bg-red-900/30 text-red-500 px-2 py-0.5 rounded border border-red-900/50 uppercase tracking-tighter">{order.orderNumber || `#${order.id}`}</span>
|
||||
<h4 className="font-bold text-lg text-white">{order.name}</h4>
|
||||
</div>
|
||||
<p className="text-sm text-stone-300 font-mono">{order.phone}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<select
|
||||
value={order.status || 'Pending'}
|
||||
onChange={(e) => API.updateOrderStatus(order.id, e.target.value).then(refreshData)}
|
||||
className="bg-stone-900 border border-stone-800 rounded px-2 py-1 text-xs text-stone-400 focus:border-red-600 outline-none"
|
||||
>
|
||||
<option value="Pending">Pending</option>
|
||||
<option value="Deposit Paid">Deposit Paid</option>
|
||||
<option value="Paid">Paid</option>
|
||||
<option value="Cooked">Cooked</option>
|
||||
<option value="Completed">Completed</option>
|
||||
<option value="Cancelled">Cancelled</option>
|
||||
</select>
|
||||
<button onClick={() => promptConfirm(`Delete order ${order.orderNumber || order.name}?`, () => API.deleteOrder(order.id).then(refreshData))} className="p-2 text-stone-600 hover:text-red-500 transition-colors bg-stone-900 rounded"><Icons.Trash /></button>
|
||||
</div>
|
||||
<button onClick={() => deleteOrder(order.id)} className="text-stone-600 hover:text-red-500 transition-colors bg-stone-900 p-2 rounded"><Icons.Trash /></button>
|
||||
</div>
|
||||
<div className="bg-stone-900 rounded p-3 border border-stone-800/50">
|
||||
<div className="bg-stone-900 rounded p-3 border border-stone-800/50 mb-4">
|
||||
{order.items.map((item: any, idx: number) => (
|
||||
<div key={idx} className="flex justify-between text-sm py-2 border-b border-stone-800 last:border-0">
|
||||
<div>
|
||||
<span className="text-stone-300 font-bold block">{item.name}</span>
|
||||
<span className="text-stone-500 text-xs">{item.qty}{item.unit} @ ${item.finalPricePerUnit || item.price}/{item.unit}</span>
|
||||
</div>
|
||||
<span className="text-stone-400 font-mono">${(item.lineTotal || 0).toFixed(2)}</span>
|
||||
<div key={idx} className="flex justify-between text-sm py-1 border-b border-stone-800/30 last:border-0">
|
||||
<span className="text-stone-300">{item.qty}{item.unit} {item.name}</span>
|
||||
<span className="text-stone-500 font-mono">${(item.lineTotal || 0).toFixed(2)}</span>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex justify-between mt-3 pt-3 border-t border-stone-800 font-bold">
|
||||
<span className="text-white">Total Est.</span>
|
||||
<span className="text-red-500">${order.estimatedTotal?.toFixed(2)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-end">
|
||||
<div className="text-[10px] text-stone-500 space-y-1">
|
||||
<p className="flex items-center gap-1 uppercase font-bold"><Icons.Clock /> {new Date(order.timestamp).toLocaleString('en-AU')}</p>
|
||||
<p className="flex items-center gap-1 uppercase font-bold"><Icons.ShoppingCart /> {order.totalWeight}kg Weight</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-[10px] text-stone-500 uppercase font-bold">Estimated Total</p>
|
||||
<p className="text-xl font-black text-red-500">${order.estimatedTotal?.toFixed(2)}</p>
|
||||
<p className="text-[10px] text-red-800 font-bold uppercase tracking-tighter">30% Deposit: ${(order.estimatedTotal * 0.3).toFixed(2)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -162,6 +182,40 @@ export default function AdminDashboard({ stock, orders, refreshData, notify, pro
|
||||
<div className="max-w-md mx-auto text-center pt-10">
|
||||
<div className="text-stone-500 mb-4 mx-auto flex justify-center"><Icons.Settings /></div>
|
||||
<h3 className="text-xl font-bold text-white mb-6">Admin Settings</h3>
|
||||
<div className="bg-stone-950 p-6 rounded-xl border border-stone-800 mb-8 space-y-6 text-left">
|
||||
<h4 className="text-sm font-bold text-red-500 uppercase tracking-widest border-b border-stone-800 pb-2">Scheduling & Deadlines</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-stone-500 uppercase mb-2">Final Deposit Date</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={stock.config?.finalDepositDate || ''}
|
||||
onChange={(e) => API.updateConfig({ finalDepositDate: e.target.value }).then(refreshData)}
|
||||
className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white focus:border-red-600 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-stone-500 uppercase mb-2">Scheduled Cook Day</label>
|
||||
<input
|
||||
type="date"
|
||||
value={stock.config?.cookDay || ''}
|
||||
onChange={(e) => API.updateConfig({ cookDay: e.target.value }).then(refreshData)}
|
||||
className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white focus:border-red-600 outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-stone-500 uppercase mb-2">PayID Details</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="e.g. 0400 000 000 or email@domain.com"
|
||||
value={stock.config?.payIdInfo || ''}
|
||||
onChange={(e) => API.updateConfig({ payIdInfo: e.target.value }).then(refreshData)}
|
||||
className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white focus:border-red-600 outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-stone-950 p-6 rounded-xl border border-stone-800 mb-8">
|
||||
<label className="block text-xs font-bold text-stone-500 uppercase mb-2 text-left">Update Admin Password</label>
|
||||
<input type="text" placeholder="New Code" value={newAdminCode} onChange={(e) => setNewAdminCode(e.target.value)} className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white mb-4 focus:border-red-600 outline-none" />
|
||||
|
||||
@@ -13,9 +13,11 @@ interface CartItem {
|
||||
bulk2Price: number;
|
||||
}
|
||||
|
||||
export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubmit }: { cart: CartItem[], onClose: () => void, onRemove: (id: number) => void, onUpdateQty: (id: number, delta: number) => void, onSubmit: (name: string, phone: string) => void }) {
|
||||
export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubmit, config }: { cart: CartItem[], onClose: () => void, onRemove: (id: number) => void, onUpdateQty: (id: number, delta: number) => void, onSubmit: (name: string, phone: string) => Promise<any>, config?: any }) {
|
||||
const [name, setName] = useState('');
|
||||
const [phone, setPhone] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submittedOrder, setSubmittedOrder] = useState<any>(null);
|
||||
|
||||
const getPrice = (item: CartItem, qty: number) => {
|
||||
if (item.bulk2Threshold > 0 && qty >= item.bulk2Threshold && item.bulk2Price > 0) return item.bulk2Price;
|
||||
@@ -29,6 +31,60 @@ export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubm
|
||||
}, 0);
|
||||
|
||||
const totalWeight = cart.reduce((acc, i) => acc + i.qty, 0);
|
||||
const depositAmount = total * 0.3;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!name || !phone) return;
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
const order = await onSubmit(name, phone);
|
||||
setSubmittedOrder(order);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (submittedOrder) {
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/95 backdrop-blur-md flex items-center justify-center p-4 z-50">
|
||||
<div className="bg-[#1c1917] border border-stone-700 w-full max-w-md rounded-2xl shadow-[0_0_50px_rgba(220,38,38,0.2)] overflow-hidden flex flex-col">
|
||||
<div className="p-8 text-center space-y-6">
|
||||
<div className="bg-green-900/20 w-20 h-20 rounded-full flex items-center justify-center mx-auto border border-green-500/50 text-green-500 text-4xl animate-bounce">
|
||||
<Icons.Check />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-3xl font-black italic text-white uppercase mb-2">Order Received!</h2>
|
||||
<p className="text-stone-400 font-mono tracking-tighter text-lg font-bold">ID: {submittedOrder.orderNumber}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-stone-950 p-6 rounded-xl border border-stone-800 space-y-4 text-left">
|
||||
<h3 className="text-red-500 font-black uppercase text-xs tracking-widest border-b border-stone-800 pb-2">Payment Instructions</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-3">
|
||||
<div className="text-stone-500 mt-1"><Icons.Alert /></div>
|
||||
<p className="text-sm text-stone-300">A <span className="text-white font-bold">30% deposit of ${depositAmount.toFixed(2)}</span> is required before we start the smoker.</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-bold text-stone-500 uppercase">Option 1: PayID (Instant)</p>
|
||||
<div className="bg-stone-900 p-3 rounded border border-red-900/30 text-white font-mono flex justify-between items-center group">
|
||||
<span>{config?.payIdInfo || '0400 000 000'}</span>
|
||||
<span className="text-[8px] uppercase bg-red-900/50 px-1 rounded text-red-400">Copy</span>
|
||||
</div>
|
||||
<p className="text-[10px] text-stone-500 italic">Please use "{submittedOrder.orderNumber}" as the payment description.</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-bold text-stone-500 uppercase">Option 2: Cash Deposit</p>
|
||||
<p className="text-sm text-stone-300">Contact us via the phone number provided to arrange a cash drop-off.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button onClick={onClose} className="w-full bg-stone-800 hover:bg-stone-700 text-white font-black uppercase py-4 rounded-xl transition-all">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/90 backdrop-blur-sm flex items-center justify-center p-4 z-50">
|
||||
@@ -97,14 +153,20 @@ export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubm
|
||||
|
||||
{cart.length > 0 && (
|
||||
<div className="p-6 bg-[#0c0a09] border-t border-stone-800">
|
||||
<div className="flex justify-between mb-6 text-xl font-bold">
|
||||
<span className="text-stone-400">Total Est. ({totalWeight}kg)</span>
|
||||
<span className="text-red-500 text-2xl">${total.toFixed(2)}</span>
|
||||
<div className="space-y-2 mb-6">
|
||||
<div className="flex justify-between text-stone-400 font-bold">
|
||||
<span>Total Est. ({totalWeight}kg)</span>
|
||||
<span>${total.toFixed(2)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-red-500 text-2xl font-black italic uppercase tracking-tighter">
|
||||
<span>30% Deposit Due</span>
|
||||
<span>${depositAmount.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="text-xs bg-stone-950 p-3 rounded border border-stone-800 text-stone-400">
|
||||
<p className="font-bold text-red-500 mb-1 uppercase">Payment Required</p>
|
||||
<p>By submitting, you agree to pay upfront via cash. Delivery/Pickup will be negotiated upon confirmation.</p>
|
||||
<p className="font-bold text-red-500 mb-1 uppercase tracking-widest">Deposit Required</p>
|
||||
<p>Order will not be cooked until the 30% deposit is paid. We accept PayID and Cash.</p>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
@@ -116,7 +178,13 @@ export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubm
|
||||
<input type="tel" required placeholder="0400 000 000" value={phone} onChange={(e) => setPhone(e.target.value)} className="w-full bg-stone-900 border border-stone-700 rounded-lg p-4 text-white focus:outline-none focus:border-red-600 focus:ring-1 focus:ring-red-600 transition-all" />
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={() => onSubmit(name, phone)} disabled={!name || !phone} className="w-full bg-red-700 hover:bg-red-600 disabled:bg-stone-800 disabled:text-stone-600 text-white font-black uppercase tracking-widest py-4 rounded-lg transition-all shadow-lg shadow-red-900/20 active:scale-[0.98]">Submit Order</button>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={!name || !phone || isSubmitting}
|
||||
className="w-full bg-red-700 hover:bg-red-600 disabled:bg-stone-800 disabled:text-stone-600 text-white font-black uppercase tracking-widest py-4 rounded-lg transition-all shadow-lg shadow-red-900/20 active:scale-[0.98] flex items-center justify-center gap-2"
|
||||
>
|
||||
{isSubmitting ? 'Sending Request...' : 'Submit Order'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -65,10 +65,23 @@ function StockCard({ item, onAdd, disabled }: { item: StockItem, onAdd?: () => v
|
||||
);
|
||||
}
|
||||
|
||||
export default function CustomerView({ stock, addToCart }: { stock: StockItem[], addToCart: (item: StockItem) => void }) {
|
||||
export default function CustomerView({ stock, addToCart, config }: { stock: StockItem[], addToCart: (item: StockItem) => void, config?: any }) {
|
||||
const availableStock = stock.filter(item => item.available);
|
||||
const soldOutStock = stock.filter(item => !item.available);
|
||||
|
||||
const formatDate = (d: string) => {
|
||||
if (!d) return 'TBA';
|
||||
try {
|
||||
return new Date(d).toLocaleString('en-AU', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
} catch (e) { return d; }
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="text-center space-y-6 py-10 bg-gradient-to-b from-stone-900/0 to-stone-900/20 rounded-3xl border border-stone-800/50">
|
||||
@@ -79,14 +92,35 @@ export default function CustomerView({ stock, addToCart }: { stock: StockItem[],
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-red-900/10 border border-red-900/40 p-5 rounded-lg flex items-start gap-3 max-w-3xl mx-auto shadow-[0_0_15px_rgba(220,38,38,0.1)]">
|
||||
<div className="text-red-500 mt-1 flex-shrink-0"><Icons.Alert /></div>
|
||||
<div>
|
||||
<p className="text-red-400 font-bold uppercase tracking-wider text-sm mb-1">Terms of Service</p>
|
||||
<p className="text-stone-300 text-sm leading-relaxed">
|
||||
<span className="text-white font-bold underline">Cash payment is required up front</span> before any smoking begins to cover material costs.
|
||||
Delivery or pickup options can be negotiated directly with us after your request is confirmed.
|
||||
</p>
|
||||
<div className="bg-red-900/10 border border-red-900/40 p-6 rounded-xl flex items-start gap-4 max-w-4xl mx-auto shadow-[0_0_25px_rgba(220,38,38,0.1)] relative overflow-hidden group">
|
||||
<div className="absolute top-0 left-0 w-1 h-full bg-red-600"></div>
|
||||
<div className="text-red-500 mt-1 flex-shrink-0 text-2xl"><Icons.Alert /></div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-red-500 font-black uppercase tracking-widest text-sm mb-2 flex items-center gap-2">
|
||||
Terms of Service & Ordering Policy
|
||||
</p>
|
||||
<p className="text-stone-300 text-sm leading-relaxed">
|
||||
To secure your order, a <span className="text-white font-bold underline">30% non-refundable deposit</span> is required upfront before smoking begins.
|
||||
Payments can be made via <span className="text-red-400 font-bold">PayID</span> or <span className="text-red-400 font-bold">Cash</span>.
|
||||
The remaining balance is payable upon pickup or delivery.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-2">
|
||||
<div className="bg-stone-950/50 border border-stone-800 p-3 rounded-lg">
|
||||
<p className="text-[10px] text-stone-500 uppercase font-black mb-1">Final Deposit Deadline</p>
|
||||
<p className="text-white font-mono font-bold">{config?.finalDepositDate ? formatDate(config.finalDepositDate) : 'TBA'}</p>
|
||||
</div>
|
||||
<div className="bg-stone-950/50 border border-stone-800 p-3 rounded-lg">
|
||||
<p className="text-[10px] text-stone-500 uppercase font-black mb-1">Scheduled Cook Day</p>
|
||||
<p className="text-red-500 font-mono font-bold uppercase tracking-tighter text-lg">{config?.cookDay ? new Date(config.cookDay).toLocaleDateString('en-AU', { weekday: 'long', day: 'numeric', month: 'long' }) : 'TBA'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-[10px] text-stone-500 italic flex items-center gap-1">
|
||||
<Icons.Check /> Deposits cover material costs and are final. PayID details provided after ordering.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import "tailwindcss";
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
export const API_BASE = import.meta.env.VITE_API_URL || '';
|
||||
|
||||
export const API = {
|
||||
fetchData: () => fetch(`${API_BASE}/api/data`).then(res => res.json()),
|
||||
submitOrder: (order: any) => fetch(`${API_BASE}/api/orders`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(order)
|
||||
}).then(res => res.json()),
|
||||
deleteOrder: (id: number) => fetch(`${API_BASE}/api/orders/${id}`, { method: 'DELETE' }),
|
||||
fetchData: async () => {
|
||||
const res = await fetch(`${API_BASE}/api/data`);
|
||||
return res.json();
|
||||
},
|
||||
submitOrder: async (order: any) => {
|
||||
const res = await fetch(`${API_BASE}/api/orders`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(order)
|
||||
});
|
||||
return res.json();
|
||||
},
|
||||
updateOrderStatus: async (id: number, status: string) => {
|
||||
const res = await fetch(`${API_BASE}/api/orders/${id}/status`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ status })
|
||||
});
|
||||
return res.json();
|
||||
},
|
||||
deleteOrder: async (id: number) => {
|
||||
await fetch(`${API_BASE}/api/orders/${id}`, { method: 'DELETE' });
|
||||
},
|
||||
clearOrders: () => fetch(`${API_BASE}/api/orders`, { method: 'DELETE' }),
|
||||
updateStock: (item: any) => fetch(`${API_BASE}/api/stock`, {
|
||||
method: 'POST',
|
||||
|
||||
Reference in new issue
Block a user