Counter Module initialized
This commit is contained in:
@@ -17,8 +17,10 @@ public class BaseItemController {
|
||||
private BaseItemRepository baseItemRepository;
|
||||
|
||||
@GetMapping
|
||||
public List<BaseItem> getAllBaseItems() {
|
||||
return baseItemRepository.findAll();
|
||||
public org.springframework.data.domain.Page<BaseItem> getAllBaseItems(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return baseItemRepository.findAll(org.springframework.data.domain.PageRequest.of(page, size));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
|
||||
@@ -17,8 +17,10 @@ public class ProductController {
|
||||
private ProductRepository productRepository;
|
||||
|
||||
@GetMapping
|
||||
public List<Product> getAllProducts() {
|
||||
return productRepository.findAllWithStalls();
|
||||
public org.springframework.data.domain.Page<Product> getAllProducts(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return productRepository.findAllWithStalls(org.springframework.data.domain.PageRequest.of(page, size));
|
||||
}
|
||||
|
||||
@GetMapping("/category/{categoryName}")
|
||||
|
||||
@@ -6,4 +6,5 @@ import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface BaseItemRepository extends JpaRepository<BaseItem, Long> {
|
||||
boolean existsByName(String name);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,11 @@ import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ProductRepository extends JpaRepository<Product, Long> {
|
||||
@Query("SELECT DISTINCT p FROM Product p LEFT JOIN FETCH p.stalls")
|
||||
List<Product> findAllWithStalls();
|
||||
@Query(value = "SELECT DISTINCT p FROM Product p LEFT JOIN FETCH p.stalls",
|
||||
countQuery = "SELECT count(p) FROM Product p")
|
||||
org.springframework.data.domain.Page<Product> findAllWithStalls(org.springframework.data.domain.Pageable pageable);
|
||||
|
||||
List<Product> findByCategory(String category);
|
||||
|
||||
boolean existsByNameAndCategory(String name, String category);
|
||||
}
|
||||
|
||||
@@ -28,13 +28,9 @@ public class DatabaseSeeder implements CommandLineRunner {
|
||||
public void run(String... args) throws Exception {
|
||||
repairStallsSchema();
|
||||
repairLobColumns();
|
||||
if (baseItemRepository.count() == 0) {
|
||||
seedCategories();
|
||||
}
|
||||
if (productRepository.count() == 0) {
|
||||
seedProducts();
|
||||
}
|
||||
}
|
||||
|
||||
private void repairStallsSchema() {
|
||||
System.out.println("Checking schema consistency for 'stalls' table...");
|
||||
@@ -81,27 +77,115 @@ public class DatabaseSeeder implements CommandLineRunner {
|
||||
}
|
||||
|
||||
private void seedCategories() {
|
||||
BaseItem breakfast = new BaseItem("Breakfast", "Morning favorites", true);
|
||||
BaseItem lunch = new BaseItem("Lunch", "Hearty meals", true);
|
||||
BaseItem snacks = new BaseItem("Snacks", "Quick bites", true);
|
||||
BaseItem beverages = new BaseItem("Beverages", "Refreshing drinks", true);
|
||||
BaseItem desserts = new BaseItem("Desserts", "Sweet treats", true);
|
||||
String[][] categories = {
|
||||
{"Fast Food", "Burgers, Pizzas and more"},
|
||||
{"Indian Main Course", "Traditional Indian meals"},
|
||||
{"Chinese", "Oriental flavors"},
|
||||
{"Beverages & Drinks", "Refreshing drinks and coffee"},
|
||||
{"Healthy & Salads", "Nutritious and light options"},
|
||||
{"Snacks & Quick Bites", "Classic street food and snacks"},
|
||||
{"Bakery & Sweets", "Cakes, pastries and desserts"}
|
||||
};
|
||||
|
||||
baseItemRepository.saveAll(List.of(breakfast, lunch, snacks, beverages, desserts));
|
||||
System.out.println("Categories seeded!");
|
||||
for (String[] cat : categories) {
|
||||
if (!baseItemRepository.existsByName(cat[0])) {
|
||||
baseItemRepository.save(new BaseItem(cat[0], cat[1], true));
|
||||
System.out.println("Seeded category: " + cat[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void seedProducts() {
|
||||
Product dosa = createProduct("Masala Dosa", "Breakfast", "Crispy dosa with potato filling", 60.0, 50, true);
|
||||
Product poha = createProduct("Poha", "Breakfast", "Flattened rice with onions and spices", 30.0, 40, true);
|
||||
Product thali = createProduct("Veg Thali", "Lunch", "Full meal with roti, sabzi, dal and rice", 120.0, 30, true);
|
||||
Product biryani = createProduct("Chicken Biryani", "Lunch", "Aromatic rice with spiced chicken", 150.0, 20, false);
|
||||
Product samosa = createProduct("Samosa", "Snacks", "Deep fried pastry with potato filling", 15.0, 100, true);
|
||||
Product tea = createProduct("Masala Tea", "Beverages", "Traditional Indian spiced tea", 10.0, 200, true);
|
||||
Product iceCream = createProduct("Vanilla Ice Cream", "Desserts", "Classic vanilla flavor", 30.0, 0, true); // Out of stock
|
||||
// Fast Food
|
||||
addProductsToCategory("Fast Food", new String[]{
|
||||
"Veg Burger", "Chicken Burger", "Cheese Burger", "Double Patty Burger", "Spicy Zinger Burger",
|
||||
"Paneer Burger", "Aloha Burger", "Mushroom Burger", "Fish Burger", "BBQ Burger",
|
||||
"Margherita Pizza", "Farmhouse Pizza", "Peppy Paneer", "Mexican Wave", "Pepper BBQ Chicken",
|
||||
"Chicken Tikka Pizza", "Meat Lover's Pizza", "Veggie Paradise", "Corn & Cheese Pizza", "Double Cheese Margherita",
|
||||
"Classic Fries", "Peri Peri Fries", "Cheese Loaded Fries", "Onion Rings", "Garlic Bread Sticks"
|
||||
}, 50.0, 350.0);
|
||||
|
||||
productRepository.saveAll(List.of(dosa, poha, thali, biryani, samosa, tea, iceCream));
|
||||
System.out.println("Products seeded!");
|
||||
// Indian Main Course
|
||||
addProductsToCategory("Indian Main Course", new String[]{
|
||||
"Paneer Butter Masala", "Kadai Paneer", "Palak Paneer", "Matar Paneer", "Paneer Tikka Masala",
|
||||
"Dal Makhani", "Dal Tadka", "Yellow Dal Fry", "Chole Masala", "Rajma Masala",
|
||||
"Veg Kolhapuri", "Mix Veg", "Malai Kofta", "Bhindi Do Pyaza", "Aloo Gobi",
|
||||
"Butter Chicken", "Chicken Tikka Masala", "Mutton Rogan Josh", "Egg Curry", "Fish Curry",
|
||||
"Jeera Rice", "Steamed Rice", "Veg Pulao", "Peas Pulao", "Kashmiri Pulao"
|
||||
}, 100.0, 450.0);
|
||||
|
||||
// Chinese
|
||||
addProductsToCategory("Chinese", new String[]{
|
||||
"Veg Hakka Noodles", "Schezwan Noodles", "Chilli Garlic Noodles", "Singapore Noodles", "Manchow Noodles",
|
||||
"Veg Fried Rice", "Egg Fried Rice", "Chicken Fried Rice", "Mixed Fried Rice", "Schezwan Fried Rice",
|
||||
"Veg Manchurian", "Chicken Manchurian", "Gobi Manchurian", "Baby Corn Manchurian", "Mushroom Manchurian",
|
||||
"Chilli Paneer", "Chilli Chicken", "Chilli Potato", "Crispy Corn", "Spring Rolls",
|
||||
"Hot and Sour Soup", "Sweet Corn Soup", "Lemon Coriander Soup", "Clear Soup", "Wonton Soup"
|
||||
}, 80.0, 280.0);
|
||||
|
||||
// Beverages & Drinks
|
||||
addProductsToCategory("Beverages & Drinks", new String[]{
|
||||
"Fresh Lime Soda", "Virgin Mojito", "Blue Lagoon", "Iced Tea", "Cold Coffee",
|
||||
"Mango Shake", "Oreo Shake", "Chocolate Shake", "Strawberry Shake", "Vanilla Shake",
|
||||
"Masala Tea", "Ginger Tea", "Green Tea", "Black Coffee", "Cappuccino", "Latte",
|
||||
"Orange Juice", "Watermelon Juice", "Pineapple Juice", "Mixed Fruit Juice", "Mosambi Juice",
|
||||
"Coca Cola", "Pepsi", "Sprite", "Fanta", "Mineral Water"
|
||||
}, 20.0, 180.0);
|
||||
|
||||
// Healthy & Salads
|
||||
addProductsToCategory("Healthy & Salads", new String[]{
|
||||
"Caesar Salad", "Greek Salad", "Garden Salad", "Fruit Salad", "Russian Salad",
|
||||
"Sprouted Moong Salad", "Chickpea Salad", "Cucumber Salad", "Tomato Onion Salad", "Beetroot Salad",
|
||||
"Grilled Chicken Salad", "Tuna Salad", "Boiled Egg Salad", "Paneer Salad", "Tofu Salad",
|
||||
"Vegetable Clear Soup", "Quinoa Bowl", "Brown Rice Bowl", "Steamed Vegetables", "Sautéed Spinach"
|
||||
}, 120.0, 320.0);
|
||||
|
||||
// Snacks & Quick Bites
|
||||
addProductsToCategory("Snacks & Quick Bites", new String[]{
|
||||
"Samosa", "Vada Pav", "Batata Vada", "Bread Pakoda", "Paneer Pakoda",
|
||||
"Veg Sandwich", "Cheese Sandwich", "Grilled Sandwich", "Corn Sandwich", "Bombay Sandwich",
|
||||
"Pani Puri", "Bhel Puri", "Sev Puri", "Dahi Puri", "Aloo Tikki",
|
||||
"Pav Bhaji", "Misal Pav", "Poha", "Upma", "Idli Sambhar",
|
||||
"Medu Vada", "Masala Dosa", "Onion Uttapam", "Plain Dosa", "Cheese Dosa"
|
||||
}, 15.0, 150.0);
|
||||
|
||||
// Bakery & Sweets
|
||||
addProductsToCategory("Bakery & Sweets", new String[]{
|
||||
"Chocolate Cake", "Pineapple Cake", "Black Forest Cake", "Red Velvet Cake", "Butterscotch Cake",
|
||||
"Chocolate Brownie", "Walnut Brownie", "Apple Pie", "Cheesecake", "Fruit Tart",
|
||||
"Gulab Jamun", "Rasgulla", "Kaju Katli", "Motichoor Ladoo", "Jalebi",
|
||||
"Vanilla Muffin", "Choco Chip Cookie", "Oatmeal Cookie", "Crossiant", "Danish Pastry"
|
||||
}, 40.0, 250.0);
|
||||
|
||||
System.out.println("Products seeding check complete!");
|
||||
}
|
||||
|
||||
private void addProductsToCategory(String category, String[] productNames, double minPrice, double maxPrice) {
|
||||
java.util.Random random = new java.util.Random();
|
||||
List<Product> productsToSave = new java.util.ArrayList<>();
|
||||
|
||||
for (String name : productNames) {
|
||||
if (!productRepository.existsByNameAndCategory(name, category)) {
|
||||
double price = minPrice + (maxPrice - minPrice) * random.nextDouble();
|
||||
price = Math.round(price / 5.0) * 5.0; // Round to nearest 5
|
||||
|
||||
int stock = random.nextInt(100);
|
||||
boolean isVeg = !name.toLowerCase().contains("chicken") &&
|
||||
!name.toLowerCase().contains("meat") &&
|
||||
!name.toLowerCase().contains("fish") &&
|
||||
!name.toLowerCase().contains("egg") &&
|
||||
!name.toLowerCase().contains("mutton") &&
|
||||
!name.toLowerCase().contains("tuna");
|
||||
|
||||
Product p = createProduct(name, category, name + " prepared fresh with quality ingredients.", price, stock, isVeg);
|
||||
p.setProductId("PRD-" + String.format("%04d", random.nextInt(10000)));
|
||||
productsToSave.add(p);
|
||||
}
|
||||
}
|
||||
if (!productsToSave.isEmpty()) {
|
||||
productRepository.saveAll(productsToSave);
|
||||
System.out.println("Seeded " + productsToSave.size() + " new products in " + category);
|
||||
}
|
||||
}
|
||||
|
||||
private Product createProduct(String name, String category, String desc, Double price, Integer stock, boolean isVeg) {
|
||||
|
||||
@@ -13,3 +13,7 @@ spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||
# Master Account Credentials
|
||||
app.master.username=admin
|
||||
app.master.password=admin
|
||||
|
||||
# File upload configuration
|
||||
spring.servlet.multipart.max-file-size=10MB
|
||||
spring.servlet.multipart.max-request-size=10MB
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Plus, X, Search, Filter, MoreVertical, RefreshCw, Edit2, Power, PowerOff, ShoppingCart, Package, ExternalLink } from 'lucide-react';
|
||||
import Pagination from '../components/Pagination';
|
||||
|
||||
interface BaseItem {
|
||||
id: number;
|
||||
@@ -23,6 +24,12 @@ const BaseMenu = () => {
|
||||
const [editingItem, setEditingItem] = useState<BaseItem | null>(null);
|
||||
const [newItem, setNewItem] = useState({ name: '', description: '', active: true });
|
||||
const [openMenuId, setOpenMenuId] = useState<number | null>(null);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
// Pagination States
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [totalElements, setTotalElements] = useState(0);
|
||||
|
||||
// Associated Products State
|
||||
const [selectedBaseItem, setSelectedBaseItem] = useState<BaseItem | null>(null);
|
||||
@@ -42,14 +49,21 @@ const BaseMenu = () => {
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
}, [currentPage, pageSize]);
|
||||
|
||||
const fetchItems = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await fetch('http://localhost:8080/api/base-items');
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/base-items?page=${currentPage}&size=${pageSize}`);
|
||||
const data = await response.json();
|
||||
setItems(data);
|
||||
if (data && data.content) {
|
||||
setItems(data.content);
|
||||
setTotalElements(data.totalElements);
|
||||
} else {
|
||||
setItems([]);
|
||||
setTotalElements(0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching items:', error);
|
||||
} finally {
|
||||
@@ -147,6 +161,8 @@ const BaseMenu = () => {
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search items..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 bg-gray-50 border border-[#e2e8f0] rounded-lg text-sm focus:outline-none focus:border-[#231651]/30 transition-all"
|
||||
/>
|
||||
</div>
|
||||
@@ -161,7 +177,7 @@ const BaseMenu = () => {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-xl border border-[#e2e8f0] shadow-sm overflow-hidden">
|
||||
<div className="bg-white rounded-xl border border-[#e2e8f0] shadow-sm overflow-hidden mb-6">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
@@ -184,7 +200,7 @@ const BaseMenu = () => {
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : items.length === 0 ? (
|
||||
) : items.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase())).length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-6 py-12 text-center text-[#64748b]">
|
||||
<p className="text-lg font-medium mb-1">No items found</p>
|
||||
@@ -192,7 +208,9 @@ const BaseMenu = () => {
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
items.map((item) => (
|
||||
items
|
||||
.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()))
|
||||
.map((item) => (
|
||||
<tr key={item.id} className="hover:bg-gray-50/50 transition-all group">
|
||||
<td className="px-6 py-4 text-sm font-medium text-[#64748b]">#{item.id}</td>
|
||||
<td className="px-6 py-4">
|
||||
@@ -263,6 +281,16 @@ const BaseMenu = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
pageSize={pageSize}
|
||||
totalElements={totalElements}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={(newSize) => {
|
||||
setPageSize(newSize);
|
||||
setCurrentPage(0);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Associated Products Modal */}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Plus, X, Search, Filter, MoreVertical, RefreshCw, Edit2, Power, PowerOff, Tag, Package, Image as ImageIcon, Barcode, DollarSign, ChevronDown, Clock, Check, Trash2, Database } from 'lucide-react';
|
||||
import Pagination from '../components/Pagination';
|
||||
|
||||
interface ProductSession {
|
||||
id?: number;
|
||||
@@ -87,6 +88,13 @@ const Products = () => {
|
||||
const [formData, setFormData] = useState<Product>(emptyProduct);
|
||||
const [allStalls, setAllStalls] = useState<Stall[]>([]);
|
||||
const [openMenuId, setOpenMenuId] = useState<number | null>(null);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
// Pagination States
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [totalElements, setTotalElements] = useState(0);
|
||||
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -101,14 +109,21 @@ const Products = () => {
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
}, [currentPage, pageSize]);
|
||||
|
||||
const fetchProducts = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await fetch('http://localhost:8080/api/products');
|
||||
const host = window.location.hostname;
|
||||
const response = await fetch(`http://${host}:8080/api/products?page=${currentPage}&size=${pageSize}`);
|
||||
const data = await response.json();
|
||||
setProducts(data);
|
||||
if (data && data.content) {
|
||||
setProducts(data.content);
|
||||
setTotalElements(data.totalElements);
|
||||
} else {
|
||||
setProducts([]);
|
||||
setTotalElements(0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching products:', error);
|
||||
} finally {
|
||||
@@ -260,6 +275,8 @@ const Products = () => {
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search products..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 bg-gray-50 border border-[#e2e8f0] rounded-lg text-sm focus:outline-none focus:border-[#231651]/30 transition-all"
|
||||
/>
|
||||
</div>
|
||||
@@ -270,7 +287,7 @@ const Products = () => {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-xl border border-[#e2e8f0] shadow-sm">
|
||||
<div className="bg-white rounded-xl border border-[#e2e8f0] shadow-sm mb-6">
|
||||
<div className="overflow-x-visible">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
@@ -285,8 +302,10 @@ const Products = () => {
|
||||
</thead>
|
||||
<tbody className="divide-y divide-[#e2e8f0]">
|
||||
{loading ? (
|
||||
<tr><td colSpan={5} className="px-6 py-12 text-center text-[#64748b]"><RefreshCw className="animate-spin inline mr-2" />Loading...</td></tr>
|
||||
) : products.map((product) => (
|
||||
<tr><td colSpan={6} className="px-6 py-12 text-center text-[#64748b]"><RefreshCw className="animate-spin inline mr-2" />Loading...</td></tr>
|
||||
) : products.filter(p => p.name.toLowerCase().includes(searchTerm.toLowerCase()) || p.category.toLowerCase().includes(searchTerm.toLowerCase())).length === 0 ? (
|
||||
<tr><td colSpan={6} className="px-6 py-12 text-center text-[#64748b]">No products found</td></tr>
|
||||
) : products.filter(p => p.name.toLowerCase().includes(searchTerm.toLowerCase()) || p.category.toLowerCase().includes(searchTerm.toLowerCase())).map((product) => (
|
||||
<tr key={product.id} className="hover:bg-gray-50/50 transition-all">
|
||||
<td className="px-6 py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -370,6 +389,16 @@ const Products = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
pageSize={pageSize}
|
||||
totalElements={totalElements}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={(newSize) => {
|
||||
setPageSize(newSize);
|
||||
setCurrentPage(0);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Product Modal */}
|
||||
|
||||
Reference in New Issue
Block a user