From 96bd334e3a2fff705441f4b5f7da2ab1bed598fb Mon Sep 17 00:00:00 2001 From: Sidharth Prabhu Date: Mon, 20 Apr 2026 09:54:52 +0530 Subject: [PATCH] QR Added to the orders screens. --- .../sales/model/GeneralDashboardData.java | 13 +- .../rit/canteen/sales/model/TrendingItem.java | 29 +++++ .../sales/repository/OrderRepository.java | 6 +- .../sales/service/DashboardService.java | 57 +++++++-- backend/src/main/resources/canteen.db | 0 frontend/package-lock.json | 10 ++ frontend/package.json | 1 + frontend/src/pages/ArchivedOrders.tsx | 29 ++++- frontend/src/pages/Dashboard.tsx | 6 +- frontend/src/pages/Orders.tsx | 116 +++++++++++++++++- frontend/src/pages/StoreDashboard.tsx | 90 ++++++++------ ordering_site/src/pages/MyOrdersScreen.css | 31 +++++ ordering_site/src/pages/MyOrdersScreen.tsx | 64 ++++++++++ ordering_site/src/pages/ProfileScreen.tsx | 15 --- 14 files changed, 394 insertions(+), 73 deletions(-) create mode 100644 backend/src/main/java/com/rit/canteen/sales/model/TrendingItem.java create mode 100644 backend/src/main/resources/canteen.db diff --git a/backend/src/main/java/com/rit/canteen/sales/model/GeneralDashboardData.java b/backend/src/main/java/com/rit/canteen/sales/model/GeneralDashboardData.java index 336b8f73..0e8257c1 100644 --- a/backend/src/main/java/com/rit/canteen/sales/model/GeneralDashboardData.java +++ b/backend/src/main/java/com/rit/canteen/sales/model/GeneralDashboardData.java @@ -7,15 +7,17 @@ public class GeneralDashboardData { private DashboardStats stats; private List> storeOverview; private List> hourlySales; - private List insights; + private List> insights; + private List trendingItems; public GeneralDashboardData() {} - public GeneralDashboardData(DashboardStats stats, List> storeOverview, List> hourlySales, List insights) { + public GeneralDashboardData(DashboardStats stats, List> storeOverview, List> hourlySales, List> insights, List trendingItems) { this.stats = stats; this.storeOverview = storeOverview; this.hourlySales = hourlySales; this.insights = insights; + this.trendingItems = trendingItems; } public DashboardStats getStats() { return stats; } @@ -27,6 +29,9 @@ public class GeneralDashboardData { public List> getHourlySales() { return hourlySales; } public void setHourlySales(List> hourlySales) { this.hourlySales = hourlySales; } - public List getInsights() { return insights; } - public void setInsights(List insights) { this.insights = insights; } + public List> getInsights() { return insights; } + public void setInsights(List> insights) { this.insights = insights; } + + public List getTrendingItems() { return trendingItems; } + public void setTrendingItems(List trendingItems) { this.trendingItems = trendingItems; } } diff --git a/backend/src/main/java/com/rit/canteen/sales/model/TrendingItem.java b/backend/src/main/java/com/rit/canteen/sales/model/TrendingItem.java new file mode 100644 index 00000000..eaae94c5 --- /dev/null +++ b/backend/src/main/java/com/rit/canteen/sales/model/TrendingItem.java @@ -0,0 +1,29 @@ +package com.rit.canteen.sales.model; + +public class TrendingItem { + private String name; + private String category; + private long qty; + private String image; + + public TrendingItem() {} + + public TrendingItem(String name, String category, long qty, String image) { + this.name = name; + this.category = category; + this.qty = qty; + this.image = image; + } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getCategory() { return category; } + public void setCategory(String category) { this.category = category; } + + public long getQty() { return qty; } + public void setQty(long qty) { this.qty = qty; } + + public String getImage() { return image; } + public void setImage(String image) { this.image = image; } +} diff --git a/backend/src/main/java/com/rit/canteen/sales/repository/OrderRepository.java b/backend/src/main/java/com/rit/canteen/sales/repository/OrderRepository.java index 650cf8aa..40530ac0 100644 --- a/backend/src/main/java/com/rit/canteen/sales/repository/OrderRepository.java +++ b/backend/src/main/java/com/rit/canteen/sales/repository/OrderRepository.java @@ -48,10 +48,10 @@ public interface OrderRepository extends JpaRepository, JpaSpecific "ORDER BY HOUR(o.createdAt)") List getHourlySales(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end); - @Query("SELECT i.productName, SUM(i.quantity), SUM(i.price * i.quantity) " + - "FROM Order o JOIN o.items i " + + @Query("SELECT p.name, p.category, SUM(i.quantity), p.imageData " + + "FROM Order o JOIN o.items i JOIN Product p ON i.productId = p.id " + "WHERE o.createdAt >= :start AND o.createdAt <= :end " + - "GROUP BY i.productName " + + "GROUP BY p.name, p.category, p.imageData " + "ORDER BY SUM(i.quantity) DESC") List getTopSellingItems(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end); } diff --git a/backend/src/main/java/com/rit/canteen/sales/service/DashboardService.java b/backend/src/main/java/com/rit/canteen/sales/service/DashboardService.java index 369044fd..360eab87 100644 --- a/backend/src/main/java/com/rit/canteen/sales/service/DashboardService.java +++ b/backend/src/main/java/com/rit/canteen/sales/service/DashboardService.java @@ -3,6 +3,7 @@ package com.rit.canteen.sales.service; import com.rit.canteen.sales.model.DashboardStats; import com.rit.canteen.sales.model.GeneralDashboardData; import com.rit.canteen.sales.model.Order; +import com.rit.canteen.sales.model.TrendingItem; import com.rit.canteen.sales.repository.OrderRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -26,6 +27,7 @@ public class DashboardService { DashboardStats stats = getDashboardStats(from, to); + System.out.println("Fetching dashboard data for range: " + from + " to " + to); // 1. Store Overview List storeData = orderRepository.getStoreOverview(from, to); List> storeOverview = new ArrayList<>(); @@ -59,17 +61,54 @@ public class DashboardService { hourlySales.add(hourMap); } - // 3. Dynamic Insights - List insights = new ArrayList<>(); - if (stats.getActiveOrders() > 0) { - insights.add(stats.getActiveOrders() + " orders today! Clearly the crowd's found their happy place πŸ’ƒπŸ•Ί"); - BigDecimal avg = stats.getTotalSales() > 0 ? BigDecimal.valueOf(stats.getTotalSales()).divide(BigDecimal.valueOf(stats.getActiveOrders()), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO; - insights.add("β‚Ή" + avg + " average order value! Proof that happy bellies don't need heavy bills 🀀πŸ”₯"); - } else { - insights.add("Waiting for the first orders of the day to roll in... β˜•"); + // 3. Trending Items + List trendingData = orderRepository.getTopSellingItems(from, to); + List trendingItems = new ArrayList<>(); + for (int i = 0; i < Math.min(trendingData.size(), 4); i++) { + Object[] row = trendingData.get(i); + String name = (String) row[0]; + String category = (String) row[1]; + long qty = ((Number) row[2]).longValue(); + String imageData = (String) row[3]; + + // Format image data for frontend + String imageUrl = imageData != null ? (imageData.startsWith("http") ? imageData : (imageData.startsWith("data:") ? imageData : "data:image/png;base64," + imageData)) : "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=200&q=80"; + + trendingItems.add(new TrendingItem(name, category, qty, imageUrl)); } - return new GeneralDashboardData(stats, storeOverview, hourlySales, insights); + // 4. Dynamic Insights + List> insights = new ArrayList<>(); + if (stats.getActiveOrders() > 0) { + Map orderInsight = new HashMap<>(); + orderInsight.put("text", stats.getActiveOrders() + " orders at RIT Canteen! Clearly the crowd's found their happy place πŸ’ƒπŸ•Ί"); + orderInsight.put("color", "bg-rose-50 text-rose-600 border-rose-100"); + insights.add(orderInsight); + + BigDecimal avg = stats.getTotalSales() > 0 ? BigDecimal.valueOf(stats.getTotalSales()).divide(BigDecimal.valueOf(stats.getActiveOrders()), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO; + Map avgInsight = new HashMap<>(); + avgInsight.put("text", "β‚Ή" + avg + " average order value! Either everyone's hungry or just living large πŸ”₯πŸ˜‹"); + avgInsight.put("color", "bg-emerald-50 text-emerald-600 border-emerald-100"); + insights.add(avgInsight); + + Map customerInsight = new HashMap<>(); + customerInsight.put("text", "RIT Canteen had " + stats.getActiveOrders() + " orders but only " + stats.getDailyCustomers() + " customers β€” Maybe customers are shy! πŸ₯°"); + customerInsight.put("color", "bg-orange-50 text-orange-600 border-orange-100"); + insights.add(customerInsight); + + Map revenueInsight = new HashMap<>(); + revenueInsight.put("text", "RIT Canteen clocked β‚Ή" + String.format("%,d", stats.getTotalSales()) + " β€” ka-ching! That's called business booming πŸ’ΈπŸ“ˆ"); + revenueInsight.put("color", "bg-blue-50 text-blue-600 border-blue-100"); + insights.add(revenueInsight); + } else { + Map emptyInsight = new HashMap<>(); + emptyInsight.put("text", "Waiting for the first orders of the day to roll in... β˜•"); + emptyInsight.put("color", "bg-indigo-50 text-indigo-600 border-indigo-100"); + insights.add(emptyInsight); + } + + System.out.println("Dashboard data generated successfully with " + trendingItems.size() + " trending items."); + return new GeneralDashboardData(stats, storeOverview, hourlySales, insights, trendingItems); } public DashboardStats getDashboardStats(LocalDateTime from, LocalDateTime to) { diff --git a/backend/src/main/resources/canteen.db b/backend/src/main/resources/canteen.db new file mode 100644 index 00000000..e69de29b diff --git a/frontend/package-lock.json b/frontend/package-lock.json index eacd174f..1e7a7ca7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,6 +15,7 @@ "jspdf": "^4.2.1", "jspdf-autotable": "^5.0.7", "lucide-react": "^1.7.0", + "qrcode.react": "^4.2.0", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router-dom": "^7.14.0", @@ -3516,6 +3517,15 @@ "node": ">=6" } }, + "node_modules/qrcode.react": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz", + "integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 22496e45..a53cac65 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "jspdf": "^4.2.1", "jspdf-autotable": "^5.0.7", "lucide-react": "^1.7.0", + "qrcode.react": "^4.2.0", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router-dom": "^7.14.0", diff --git a/frontend/src/pages/ArchivedOrders.tsx b/frontend/src/pages/ArchivedOrders.tsx index f86bb729..3d992850 100644 --- a/frontend/src/pages/ArchivedOrders.tsx +++ b/frontend/src/pages/ArchivedOrders.tsx @@ -15,6 +15,7 @@ import { ArrowLeft } from 'lucide-react'; import { format, subDays } from 'date-fns'; +import { QRCodeCanvas } from 'qrcode.react'; import Pagination from '../components/Pagination'; interface OrderItem { @@ -336,7 +337,7 @@ const ArchivedOrders: React.FC = () => { {/* Bottom Totals Summary */}
-
+
Unique Items
{selectedOrder.items.length}
@@ -347,6 +348,32 @@ const ArchivedOrders: React.FC = () => { Closed File
+ + {/* QR Verification with Status Blur */} +
+
+
+ +
+ {['COMPLETED', 'CANCELLED'].includes(selectedOrder.status.toUpperCase()) && ( +
+ + {selectedOrder.status.toUpperCase() === 'COMPLETED' ? 'DELIVERED' : 'EXPIRED'} + +
+ )} +
+
+
Verification
+
Ledger Sync QR
+
+
Final Settlement
diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index eb95cc5c..c2fe1a09 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -328,15 +328,15 @@ const Dashboard = () => {

Business Intelligence

- {insights.map((insight: string, idx: number) => ( + {insights.map((insight: any, idx: number) => ( -

{insight}

+

{insight.text || insight}

))}
diff --git a/frontend/src/pages/Orders.tsx b/frontend/src/pages/Orders.tsx index c6f9e4cf..3102d005 100644 --- a/frontend/src/pages/Orders.tsx +++ b/frontend/src/pages/Orders.tsx @@ -15,9 +15,11 @@ import { Check, X as XIcon, RotateCcw, - Edit2 + Edit2, + Printer } from 'lucide-react'; import { format } from 'date-fns'; +import { QRCodeCanvas } from 'qrcode.react'; import Pagination from '../components/Pagination'; interface OrderItem { @@ -75,6 +77,8 @@ const Orders: React.FC = () => { const [editSearchQuery, setEditSearchQuery] = useState(''); const [editingItems, setEditingItems] = useState([]); const [isUpdatingOrder, setIsUpdatingOrder] = useState(false); + const [showQRMenu, setShowQRMenu] = useState(false); + const [isRegenerating, setIsRegenerating] = useState(false); // Debounce search query useEffect(() => { @@ -249,6 +253,72 @@ const Orders: React.FC = () => { } }; + const handlePrintQR = () => { + const canvas = document.querySelector('.qr-canvas canvas') as HTMLCanvasElement; + if (!canvas || !selectedOrder) return; + + const qrDataURL = canvas.toDataURL('image/png'); + const printWindow = window.open('', '_blank'); + if (printWindow) { + printWindow.document.write(` + + + Print QR Code - Order #${selectedOrder.displayOrderId} + + + + +
+

Order #${selectedOrder.displayOrderId}

+

Scan to verify at terminal

+

Customer: ${selectedOrder.user?.name || 'Walk-in'}

+
+ + + + `); + printWindow.document.close(); + } + }; + + 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) { + fetchOrders(); // This will refresh the whole list and the selected order + setShowQRMenu(false); + } else { + alert('Failed to regenerate sync ID'); + } + } catch (error) { + console.error('Error regenerating QR:', error); + } finally { + setIsRegenerating(false); + } + }; + const filteredProducts = useMemo(() => { if (!editSearchQuery.trim()) return []; return allProducts.filter(p => @@ -583,6 +653,50 @@ const Orders: React.FC = () => { {selectedOrder.items.reduce((sum, item) => sum + item.quantity, 0)}
+ + {/* QR Verification Section */} +
+
+
setShowQRMenu(!showQRMenu)} + className="p-1.5 bg-white rounded-lg border border-slate-200 shadow-sm transition-all hover:scale-105 hover:border-indigo-300 cursor-pointer qr-canvas relative group" + > + +
+ +
+
+ + {showQRMenu && ( +
+
Sync Actions
+ + +
+ )} +
+
+
Verify Order
+
Scan for Terminal Sync
+
+
diff --git a/frontend/src/pages/StoreDashboard.tsx b/frontend/src/pages/StoreDashboard.tsx index cb704305..65d39684 100644 --- a/frontend/src/pages/StoreDashboard.tsx +++ b/frontend/src/pages/StoreDashboard.tsx @@ -21,75 +21,91 @@ import { } from 'recharts'; import { motion } from 'framer-motion'; -const salesData = [ - { time: '12am', value: 0 }, - { time: '2am', value: 0 }, - { time: '4am', value: 0 }, - { time: '6am', value: 0 }, - { time: '8am', value: 5000 }, - { time: '10am', value: 16000 }, - { time: '12pm', value: 6000 }, -]; - -const trendingItems = [ - { name: "GHEE ROAST", category: "BREAK FAST", qty: 64, image: "https://images.unsplash.com/photo-1668236543090-82eba5ee5976?w=200&q=80" }, - { name: "KAL DOSA (2pcs)", category: "BREAK FAST", qty: 60, image: "https://images.unsplash.com/photo-1589301760014-d929f3979dbc?w=200&q=80" }, - { name: "VEG S.W", category: "SNACKS", qty: 51, image: "https://images.unsplash.com/photo-1528735602780-2552fd46c7af?w=200&q=80" }, - { name: "PLAIN DOSA", category: "BREAK FAST", qty: 51, image: "https://images.unsplash.com/photo-1610192244261-3f33de3f55e4?w=200&q=80" } -]; const StoreDashboard = () => { const [activeTab, setActiveTab] = useState('Sales'); const [timeRange, setTimeRange] = useState('Today'); const [stats, setStats] = useState({ - totalSales: 48438, - activeOrders: 912, - dailyCustomers: 813, - revenueGrowth: 12.5 + totalSales: 0, + activeOrders: 0, + dailyCustomers: 0, + revenueGrowth: 0 }); + const [trendingItems, setTrendingItems] = useState([]); + const [salesData, setSalesData] = useState([]); + const [insights, setInsights] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + const formatCurrency = (val: any) => { + const num = Number(val); + return isNaN(num) ? '0' : num.toLocaleString(); + }; useEffect(() => { const fetchStats = async () => { try { + setIsLoading(true); const response = await fetch('/api/dashboard/stats'); if (response.ok) { const data = await response.json(); - // Find RIT Canteen in storeOverview if available, otherwise use general stats + console.log('Dashboard data received successfully:', data); + + // Set basic stats from general stats if available + if (data.stats) { + setStats({ + totalSales: data.stats.totalSales, + activeOrders: data.stats.activeOrders, + dailyCustomers: data.stats.dailyCustomers, + revenueGrowth: data.stats.growth + }); + } + + // Override with RIT Canteen specific data if found in overview if (data.storeOverview && data.storeOverview.length > 0) { const ritStore = data.storeOverview.find((s: any) => s.name === 'RIT Canteen'); if (ritStore) { - setStats({ + setStats(prev => ({ + ...prev, totalSales: ritStore.sale, activeOrders: ritStore.orders, dailyCustomers: ritStore.orders * 0.9, // Approximation - revenueGrowth: 12.5 - }); - } else { - setStats(data.stats); + })); } - } else { - setStats(data.stats); } + + // Set other dynamic data + if (data.trendingItems) setTrendingItems(data.trendingItems); + if (data.hourlySales) setSalesData(data.hourlySales); + if (data.insights) setInsights(data.insights); } } catch (error) { console.error('Error fetching dashboard stats:', error); + } finally { + setIsLoading(false); } }; fetchStats(); }, []); const pieData = [ - { name: 'Full Payment', value: stats.totalSales, color: '#8b5cf6' }, + { name: 'Full Payment', value: Number(stats.totalSales) || 0, color: '#8b5cf6' }, { name: 'Credit', value: 0, color: '#fbbf24' } ]; - const insights = [ - { text: `${stats.activeOrders} orders at RIT Canteen! Clearly the crowd's found their happy place πŸ’ƒπŸ•Ί`, color: "bg-rose-50 text-rose-600 border-rose-100" }, - { text: `β‚Ή${(stats.totalSales / (stats.activeOrders || 1)).toFixed(2)} average order value at RIT Canteen! Either everyone's hungry or just living large πŸ”₯πŸ˜‹`, color: "bg-emerald-50 text-emerald-600 border-emerald-100" }, - { text: `RIT Canteen had ${stats.activeOrders} orders but only ${Math.round(stats.dailyCustomers)} customers β€” Maybe customers are shy and didn't give their names πŸ₯°`, color: "bg-orange-50 text-orange-600 border-orange-100" }, - { text: `RIT Canteen clocked β‚Ή${stats.totalSales.toLocaleString()} β€” ka-ching! That's called business booming πŸ’ΈπŸ“ˆ`, color: "bg-blue-50 text-blue-600 border-blue-100" }, - { text: "RIT Canteen reaches its morning sales peak around 9AM β€” breakfast rush in full swing 🍳πŸ₯ž", color: "bg-indigo-50 text-indigo-600 border-indigo-100" } - ]; + if (isLoading) { + return ( +
+ + Analyzing Store Data... + +
+ ); + } + return (
@@ -200,7 +216,7 @@ const StoreDashboard = () => {
-

β‚Ή{stats.totalSales.toLocaleString()}

+

β‚Ή{formatCurrency(stats.totalSales)}

diff --git a/ordering_site/src/pages/MyOrdersScreen.css b/ordering_site/src/pages/MyOrdersScreen.css index ff151647..acfb5fa7 100644 --- a/ordering_site/src/pages/MyOrdersScreen.css +++ b/ordering_site/src/pages/MyOrdersScreen.css @@ -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; diff --git a/ordering_site/src/pages/MyOrdersScreen.tsx b/ordering_site/src/pages/MyOrdersScreen.tsx index 9acab509..11522029 100644 --- a/ordering_site/src/pages/MyOrdersScreen.tsx +++ b/ordering_site/src/pages/MyOrdersScreen.tsx @@ -41,6 +41,7 @@ const MyOrdersScreen: React.FC = () => { const [showStockAlert, setShowStockAlert] = useState(false); const [unavailableItems, setUnavailableItems] = useState([]); const [pendingItems, setPendingItems] = useState([]); + 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'}

+
diff --git a/ordering_site/src/pages/ProfileScreen.tsx b/ordering_site/src/pages/ProfileScreen.tsx index 4ba8be3a..234a75b2 100644 --- a/ordering_site/src/pages/ProfileScreen.tsx +++ b/ordering_site/src/pages/ProfileScreen.tsx @@ -105,21 +105,6 @@ const ProfileScreen: React.FC = () => {
- {/* Stats Section */} -
-
navigate('/orders')}> - -- - Orders -
-
- β‚Ή0 - Spent -
-
- 0 - Coins -
-
{/* Menu Section */}