QR Added to the orders screens.

This commit is contained in:
Sidharth Prabhu
2026-04-20 09:54:52 +05:30
parent 12144d41c1
commit 96bd334e3a
14 changed files with 394 additions and 73 deletions

View File

@@ -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;

View File

@@ -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">

View File

@@ -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">