QR Added to the orders screens.
This commit is contained in:
@@ -258,6 +258,37 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.regenerate-sync-btn {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: white;
|
||||
border: 1px solid var(--border);
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.2s ease;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.regenerate-sync-btn:active {
|
||||
transform: scale(0.95);
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.regenerate-sync-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.regenerate-sync-btn.loading {
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.modal-info-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -41,6 +41,7 @@ const MyOrdersScreen: React.FC = () => {
|
||||
const [showStockAlert, setShowStockAlert] = useState(false);
|
||||
const [unavailableItems, setUnavailableItems] = useState<string[]>([]);
|
||||
const [pendingItems, setPendingItems] = useState<FoodItem[]>([]);
|
||||
const [isRegenerating, setIsRegenerating] = useState(false);
|
||||
|
||||
const isOrderExpired = (order: Order) => {
|
||||
if (order.isArchived) return true;
|
||||
@@ -55,6 +56,27 @@ const MyOrdersScreen: React.FC = () => {
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
// Polling for live updates (Sync when Dashboard regenerates QR or changes status)
|
||||
useEffect(() => {
|
||||
let interval: NodeJS.Timeout;
|
||||
|
||||
// Only poll if there are active (non-finalized) orders
|
||||
const hasActiveOrders = orders.some(o =>
|
||||
o.status.toUpperCase() !== 'COMPLETED' &&
|
||||
o.status.toUpperCase() !== 'CANCELLED'
|
||||
);
|
||||
|
||||
if (user?.id && hasActiveOrders) {
|
||||
interval = setInterval(() => {
|
||||
fetchOrders();
|
||||
}, 10000); // Poll every 10 seconds
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (interval) clearInterval(interval);
|
||||
};
|
||||
}, [user?.id, orders]);
|
||||
|
||||
const fetchOrders = async () => {
|
||||
try {
|
||||
const response = await fetch(`http://${window.location.hostname}:8080/api/orders/user/${user?.id}`);
|
||||
@@ -106,6 +128,40 @@ const MyOrdersScreen: React.FC = () => {
|
||||
navigate('/cart');
|
||||
};
|
||||
|
||||
const handleRegenerateQR = async () => {
|
||||
if (!selectedOrder) return;
|
||||
setIsRegenerating(true);
|
||||
try {
|
||||
const newOrderNumber = `ORD-${Math.random().toString(36).substring(2, 10).toUpperCase()}`;
|
||||
const response = await fetch(`http://${window.location.hostname}:8080/api/orders/${selectedOrder.id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
...selectedOrder,
|
||||
orderNumber: newOrderNumber
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
await fetchOrders();
|
||||
// Since selectedOrder is used in the modal, we need to update it as well
|
||||
const updated = orders.find(o => o.id === selectedOrder.id);
|
||||
if (updated) setSelectedOrder({ ...updated, orderNumber: newOrderNumber });
|
||||
else fetchOrders().then(() => {
|
||||
// Fallback refresh to catch the update
|
||||
setOrders(prev => prev.map(o => o.id === selectedOrder.id ? { ...o, orderNumber: newOrderNumber } : o));
|
||||
setSelectedOrder(prev => prev ? { ...prev, orderNumber: newOrderNumber } : null);
|
||||
});
|
||||
} else {
|
||||
alert('Failed to regenerate sync code');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error regenerating QR:', error);
|
||||
} finally {
|
||||
setIsRegenerating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleProceedPartial = () => {
|
||||
performRepeat(pendingItems);
|
||||
setShowStockAlert(false);
|
||||
@@ -265,6 +321,14 @@ const MyOrdersScreen: React.FC = () => {
|
||||
? 'This order has been fulfilled'
|
||||
: 'Show this QR code at the counter'}
|
||||
</p>
|
||||
<button
|
||||
className={`regenerate-sync-btn ${isRegenerating ? 'loading' : ''}`}
|
||||
onClick={handleRegenerateQR}
|
||||
disabled={isRegenerating || isOrderExpired(selectedOrder) || selectedOrder.status.toUpperCase() === 'COMPLETED'}
|
||||
>
|
||||
<RefreshCcw size={14} className={isRegenerating ? 'animate-spin' : ''} />
|
||||
{isRegenerating ? 'Generating...' : 'Regenerate Sync ID'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="modal-info-list">
|
||||
|
||||
@@ -105,21 +105,6 @@ const ProfileScreen: React.FC = () => {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats Section */}
|
||||
<section className="profile-stats">
|
||||
<div className="stat-box" onClick={() => navigate('/orders')}>
|
||||
<span className="stat-value">--</span>
|
||||
<span className="stat-label">Orders</span>
|
||||
</div>
|
||||
<div className="stat-box">
|
||||
<span className="stat-value">₹0</span>
|
||||
<span className="stat-label">Spent</span>
|
||||
</div>
|
||||
<div className="stat-box">
|
||||
<span className="stat-value">0</span>
|
||||
<span className="stat-label">Coins</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Menu Section */}
|
||||
<section className="profile-menu">
|
||||
|
||||
Reference in New Issue
Block a user