From 0e12702fa406b79f3417b79997cd0dfd31cff2f6 Mon Sep 17 00:00:00 2001 From: Sidharth Prabhu Date: Thu, 16 Apr 2026 11:45:08 +0530 Subject: [PATCH] Dashboard functionalities extended --- .gitignore | 1 + .vscode/settings.json | 3 +- .../sales/controller/DashboardController.java | 7 +- .../canteen/sales/model/DashboardStats.java | 7 +- .../sales/repository/OrderRepository.java | 4 + .../sales/service/DashboardService.java | 23 ++-- frontend/src/pages/Dashboard.tsx | 124 ++++++++++++++---- 7 files changed, 129 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index 6845aa9f..94d8f6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ ordering-site/* ordering_site/ +counter-frontend/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e0120650..7e9f1ebc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "java.compile.nullAnalysis.mode": "automatic", - "java.configuration.updateBuildConfiguration": "interactive" + "java.configuration.updateBuildConfiguration": "interactive", + "git.ignoreLimitWarning": true } \ No newline at end of file diff --git a/backend/src/main/java/com/rit/canteen/sales/controller/DashboardController.java b/backend/src/main/java/com/rit/canteen/sales/controller/DashboardController.java index 69545ba8..366bc717 100644 --- a/backend/src/main/java/com/rit/canteen/sales/controller/DashboardController.java +++ b/backend/src/main/java/com/rit/canteen/sales/controller/DashboardController.java @@ -6,6 +6,7 @@ import com.rit.canteen.sales.service.DashboardService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @@ -18,8 +19,10 @@ public class DashboardController { private DashboardService dashboardService; @GetMapping("/stats") - public GeneralDashboardData getStats() { - return dashboardService.getGeneralDashboardData(); + public GeneralDashboardData getStats( + @RequestParam(required = false) @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME) java.time.LocalDateTime from, + @RequestParam(required = false) @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME) java.time.LocalDateTime to) { + return dashboardService.getGeneralDashboardData(from, to); } @GetMapping("/recent-orders") diff --git a/backend/src/main/java/com/rit/canteen/sales/model/DashboardStats.java b/backend/src/main/java/com/rit/canteen/sales/model/DashboardStats.java index d3014e9f..50b6406c 100644 --- a/backend/src/main/java/com/rit/canteen/sales/model/DashboardStats.java +++ b/backend/src/main/java/com/rit/canteen/sales/model/DashboardStats.java @@ -2,14 +2,16 @@ package com.rit.canteen.sales.model; public class DashboardStats { private long totalSales; + private long periodRevenue; private int activeOrders; private int dailyCustomers; private double growth; public DashboardStats() {} - public DashboardStats(long totalSales, int activeOrders, int dailyCustomers, double growth) { + public DashboardStats(long totalSales, long periodRevenue, int activeOrders, int dailyCustomers, double growth) { this.totalSales = totalSales; + this.periodRevenue = periodRevenue; this.activeOrders = activeOrders; this.dailyCustomers = dailyCustomers; this.growth = growth; @@ -18,6 +20,9 @@ public class DashboardStats { public long getTotalSales() { return totalSales; } public void setTotalSales(long totalSales) { this.totalSales = totalSales; } + public long getPeriodRevenue() { return periodRevenue; } + public void setPeriodRevenue(long periodRevenue) { this.periodRevenue = periodRevenue; } + public int getActiveOrders() { return activeOrders; } public void setActiveOrders(int activeOrders) { this.activeOrders = activeOrders; } 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 e6bfc9fc..b46d47c6 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 @@ -14,6 +14,7 @@ import java.util.Optional; public interface OrderRepository extends JpaRepository, JpaSpecificationExecutor { List findByUserIdOrderByCreatedAtDesc(Long userId); long countByCreatedAtGreaterThanEqual(LocalDateTime startOfDay); + long countByCreatedAtBetween(LocalDateTime start, LocalDateTime end); List findByIsArchivedFalseAndCreatedAtBefore(LocalDateTime timestamp); Optional findByOrderNumber(String orderNumber); @@ -23,6 +24,9 @@ public interface OrderRepository extends JpaRepository, JpaSpecific @Query("SELECT COUNT(DISTINCT o.userId) FROM Order o WHERE o.createdAt >= :startOfDay") long countUniqueUsersToday(@Param("startOfDay") LocalDateTime startOfDay); + @Query("SELECT COUNT(DISTINCT o.userId) FROM Order o WHERE o.createdAt >= :start AND o.createdAt <= :end") + long countUniqueUsersInRange(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end); + @Query("SELECT SUM(o.totalAmount) FROM Order o WHERE o.createdAt >= :start AND o.createdAt <= :end") BigDecimal getRevenuePerPeriod(@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 6cc62986..369044fd 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 @@ -20,14 +20,14 @@ public class DashboardService { @Autowired private OrderRepository orderRepository; - public GeneralDashboardData getGeneralDashboardData() { - LocalDateTime startOfToday = LocalDate.now().atStartOfDay(); - LocalDateTime endOfToday = LocalDate.now().atTime(LocalTime.MAX); + public GeneralDashboardData getGeneralDashboardData(LocalDateTime from, LocalDateTime to) { + if (from == null) from = LocalDate.now().atStartOfDay(); + if (to == null) to = LocalDate.now().atTime(LocalTime.MAX); - DashboardStats stats = getDashboardStats(); + DashboardStats stats = getDashboardStats(from, to); // 1. Store Overview - List storeData = orderRepository.getStoreOverview(startOfToday, endOfToday); + List storeData = orderRepository.getStoreOverview(from, to); List> storeOverview = new ArrayList<>(); for (Object[] row : storeData) { Map store = new HashMap<>(); @@ -40,7 +40,7 @@ public class DashboardService { } // 2. Hourly Sales - List hourlyData = orderRepository.getHourlySales(startOfToday, endOfToday); + List hourlyData = orderRepository.getHourlySales(from, to); List> hourlySales = new ArrayList<>(); // Initialize 24 hours for (int i = 0; i < 24; i += 2) { @@ -72,7 +72,7 @@ public class DashboardService { return new GeneralDashboardData(stats, storeOverview, hourlySales, insights); } - public DashboardStats getDashboardStats() { + public DashboardStats getDashboardStats(LocalDateTime from, LocalDateTime to) { LocalDateTime startOfToday = LocalDate.now().atStartOfDay(); LocalDateTime endOfToday = LocalDate.now().atTime(LocalTime.MAX); @@ -82,8 +82,11 @@ public class DashboardService { BigDecimal totalRevenueRaw = orderRepository.getTotalRevenue(); long totalSales = totalRevenueRaw != null ? totalRevenueRaw.longValue() : 0; - int activeOrders = (int) orderRepository.countByCreatedAtGreaterThanEqual(startOfToday); - int dailyCustomers = (int) orderRepository.countUniqueUsersToday(startOfToday); + BigDecimal periodRevenueRaw = orderRepository.getRevenuePerPeriod(from, to); + long periodRevenue = periodRevenueRaw != null ? periodRevenueRaw.longValue() : 0; + + int activeOrders = (int) orderRepository.countByCreatedAtBetween(from, to); + int dailyCustomers = (int) orderRepository.countUniqueUsersInRange(from, to); BigDecimal todayRevenue = orderRepository.getRevenuePerPeriod(startOfToday, endOfToday); BigDecimal yesterdayRevenue = orderRepository.getRevenuePerPeriod(startOfYesterday, endOfYesterday); @@ -99,7 +102,7 @@ public class DashboardService { growth = 100.0; } - return new DashboardStats(totalSales, activeOrders, dailyCustomers, growth); + return new DashboardStats(totalSales, periodRevenue, activeOrders, dailyCustomers, growth); } public List getRecentOrders() { diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 323f5ec2..37aba2c2 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -23,14 +23,66 @@ import { motion } from 'framer-motion'; const Dashboard = () => { const [activeTab, setActiveTab] = useState('Sales'); const [timeRange, setTimeRange] = useState('Today'); + const [customDates, setCustomDates] = useState({ from: '', to: '' }); const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); + const getRangeDates = (range: string) => { + const now = new Date(); + const start = new Date(); + const end = new Date(); + + // Set end to end of today + end.setHours(23, 59, 59, 999); + + switch (range) { + case 'Today': + start.setHours(0, 0, 0, 0); + break; + case 'Yesterday': + start.setDate(now.getDate() - 1); + start.setHours(0, 0, 0, 0); + end.setDate(now.getDate() - 1); + end.setHours(23, 59, 59, 999); + break; + case 'Week': + start.setDate(now.getDate() - 7); + start.setHours(0, 0, 0, 0); + break; + case '30 Days': + start.setDate(now.getDate() - 30); + start.setHours(0, 0, 0, 0); + break; + case 'Custom': + if (customDates.from && customDates.to) { + const from = new Date(customDates.from); + from.setHours(0, 0, 0, 0); + const to = new Date(customDates.to); + to.setHours(23, 59, 59, 999); + return { from: from.toISOString(), to: to.toISOString() }; + } + return null; + default: + start.setHours(0, 0, 0, 0); + } + return { from: start.toISOString(), to: end.toISOString() }; + }; + useEffect(() => { const fetchData = async () => { try { setIsLoading(true); - const response = await fetch('/api/dashboard/stats'); + const range = getRangeDates(timeRange); + let url = '/api/dashboard/stats'; + + if (range) { + const params = new URLSearchParams(); + params.append('from', range.from); + params.append('to', range.to); + url += `?${params.toString()}`; + } + + const response = await fetch(url); if (response.ok) { const result = await response.json(); setData(result); @@ -42,7 +94,7 @@ const Dashboard = () => { } }; fetchData(); - }, []); + }, [timeRange, customDates]); if (isLoading || !data) { return ( @@ -61,8 +113,8 @@ const Dashboard = () => { const { stats, storeOverview, hourlySales, insights } = data; const pieData = [ - { name: 'Full Payment', value: stats.totalSales, color: '#8b5cf6' }, - { name: 'Credit', value: 0, color: '#fbbf24' } // Credits logic can be added later + { name: 'Full Payment', value: stats.periodRevenue, color: '#8b5cf6' }, + { name: 'Credit', value: 0, color: '#fbbf24' } ]; return ( @@ -115,12 +167,12 @@ const Dashboard = () => { ))} -
-
-
- Today's Revenue -
-
+
+
+
+ {timeRange}'s Revenue +
+
@@ -170,10 +222,11 @@ const Dashboard = () => { -
-

₹{stats.totalSales.toLocaleString()}

-

Life Time

-
+
+

₹{stats.periodRevenue.toLocaleString()}

+

{timeRange === 'Today' ? 'Today' : timeRange}

+

Total: ₹{stats.totalSales.toLocaleString()}

+
{pieData.map(item => (
@@ -188,18 +241,37 @@ const Dashboard = () => { {/* Right Stats Column */}
{/* Filters */} -
- {['Yesterday', 'Today', 'Week', '30 Days'].map(range => ( - - ))} -
+
+
+ {['Yesterday', 'Today', 'Week', '30 Days', 'Custom'].map(range => ( + + ))} +
+ {timeRange === 'Custom' && ( +
+ setCustomDates({ ...customDates, from: e.target.value })} + className="flex-1 bg-white border border-slate-200 rounded-lg px-2 py-1.5 text-[10px] font-bold text-slate-600 outline-none focus:border-[#0f4475]" + /> + to + setCustomDates({ ...customDates, to: e.target.value })} + className="flex-1 bg-white border border-slate-200 rounded-lg px-2 py-1.5 text-[10px] font-bold text-slate-600 outline-none focus:border-[#0f4475]" + /> +
+ )} +
{/* Total Orders Card */}