Revenue functionality added and Removed the Expenses page

This commit is contained in:
Sidharth Prabhu
2026-06-25 11:15:14 +05:30
parent 801dd22be1
commit a6285db435
32 changed files with 1639 additions and 767 deletions

View File

@@ -1,13 +1,10 @@
import React from 'react';
import { Outlet, Link, useLocation, useNavigate } from 'react-router-dom';
import {
LayoutDashboard,
ShoppingBag,
History,
Layers,
Package,
LogOut,
Menu,
Bell,
Search,
User

View File

@@ -1,11 +1,10 @@
import React, { useState, useEffect } from 'react';
import { useState, useEffect } from 'react';
import {
Layers,
Package,
Search,
Plus,
Edit2,
Trash2,
X,
Check
} from 'lucide-react';

View File

@@ -501,7 +501,7 @@ const Orders: React.FC = () => {
</div>
<div className="text-right">
<div className="text-[10px] text-slate-400 font-medium uppercase mb-0.5">Grand Total</div>
<div className="text-lg font-black text-slate-900 leading-none">{order.totalAmount.toFixed(2)}</div>
<div className="text-lg font-black text-slate-900 leading-none">🅡{order.totalAmount.toFixed(2)}</div>
</div>
</div>
@@ -674,9 +674,9 @@ const Orders: React.FC = () => {
</div>
<div className="flex items-center gap-8">
<div className="text-right">
<div className="text-sm font-black text-slate-900">{(item.price * item.quantity).toLocaleString()}</div>
<div className="text-sm font-black text-slate-900">🅡{(item.price * item.quantity).toLocaleString()}</div>
<div className="text-[10px] font-bold text-slate-400">
{item.quantity} x <span className="text-primary font-black">{item.price}</span>
{item.quantity} x <span className="text-primary font-black">🅡{item.price}</span>
</div>
</div>
</div>
@@ -744,7 +744,7 @@ const Orders: React.FC = () => {
<div className="text-right flex flex-col gap-1 min-w-[250px]">
<div className="text-[10px] text-primary/80 uppercase font-black tracking-widest mb-1">Active Grand Total</div>
<div className="text-4xl font-black text-emerald-400 leading-none mb-1">{selectedOrder.totalAmount.toLocaleString()}</div>
<div className="text-4xl font-black text-emerald-400 leading-none mb-1">🅡{selectedOrder.totalAmount.toLocaleString()}</div>
<div className="text-[10px] text-white/40 font-bold uppercase tracking-[0.2em]">{selectedOrder.status === 'COMPLETED' ? 'Transaction Completed' : 'Transaction Pending Approval'}</div>
</div>
</div>
@@ -828,7 +828,7 @@ const Orders: React.FC = () => {
</div>
<div>
<div className="font-bold text-slate-800 text-sm">{item.productName}</div>
<div className="text-[10px] font-black text-primary uppercase">{item.price} each</div>
<div className="text-[10px] font-black text-primary uppercase">🅡{item.price} each</div>
</div>
</div>
@@ -849,7 +849,7 @@ const Orders: React.FC = () => {
</button>
</div>
<div className="text-right min-w-[80px]">
<div className="text-sm font-black text-slate-900 leading-none mb-1">{(item.price * item.quantity).toFixed(2)}</div>
<div className="text-sm font-black text-slate-900 leading-none mb-1">🅡{(item.price * item.quantity).toFixed(2)}</div>
<button
onClick={() => removeItem(idx)}
className="text-[10px] font-black text-rose-400 hover:text-rose-600 uppercase tracking-widest transition-colors flex items-center gap-1 active:scale-95 cursor-pointer mx-auto"
@@ -892,7 +892,7 @@ const Orders: React.FC = () => {
<div className="font-bold text-slate-800 text-sm group-hover:text-primary transition-colors">{product.name}</div>
<div className="text-[10px] font-black text-slate-400 uppercase tracking-widest">{product.category}</div>
</div>
<div className="font-black text-emerald-600 text-xs">{product.price}</div>
<div className="font-black text-emerald-600 text-xs">🅡{product.price}</div>
</button>
))
) : editSearchQuery ? (
@@ -910,7 +910,7 @@ const Orders: React.FC = () => {
<div className="mt-8 pt-8 border-t border-slate-200">
<div className="flex justify-between items-center mb-4">
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest">New Order Total</span>
<span className="text-2xl font-black text-emerald-600 tracking-tighter">{editTotal.toFixed(2)}</span>
<span className="text-2xl font-black text-emerald-600 tracking-tighter">🅡{editTotal.toFixed(2)}</span>
</div>
<button
onClick={saveOrderEdits}

View File

@@ -1,14 +1,14 @@
import React, { useState, useEffect, useMemo } from 'react';
import {
Search,
ShoppingBag,
Trash2,
Plus,
Minus,
UtensilsCrossed,
Coffee,
IceCream,
Pizza,
import { useState, useEffect, useMemo } from 'react';
import {
Search,
ShoppingBag,
Trash2,
Plus,
Minus,
UtensilsCrossed,
Coffee,
IceCream,
Pizza,
Cake,
Phone,
Loader2
@@ -59,7 +59,7 @@ const POS = () => {
const data = await prodRes.json();
setProducts(data.content || []);
}
if (catRes.ok) {
const data = await catRes.json();
setCategories(['ALL', ...data]);
@@ -85,7 +85,7 @@ const POS = () => {
const addToCart = (product: Product) => {
const existing = cart.find(item => item.id === product.id);
if (existing) {
setCart(cart.map(item =>
setCart(cart.map(item =>
item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item
));
} else {
@@ -111,7 +111,7 @@ const POS = () => {
const handleCompleteOrder = async () => {
if (cart.length === 0) return;
setLoading(true);
try {
const host = window.location.hostname;
@@ -128,10 +128,12 @@ const POS = () => {
}))
};
const token = sessionStorage.getItem('counterToken');
const response = await fetch(`http://${host}:8080/api/orders`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {})
},
body: JSON.stringify(orderData),
});
@@ -143,16 +145,21 @@ const POS = () => {
} else {
let errorMessage = "Unknown error";
try {
const err = await response.json();
errorMessage = err.error || err.message || JSON.stringify(err);
const responseText = await response.text();
try {
const err = JSON.parse(responseText);
errorMessage = err.error || err.message || JSON.stringify(err);
} catch (e) {
errorMessage = responseText || response.statusText;
}
} catch (e) {
errorMessage = await response.text() || response.statusText;
errorMessage = response.statusText;
}
alert(`Failed to place order: ${errorMessage}`);
}
} catch (error) {
console.error("Order error:", error);
alert("Network error while placing order.");
console.error("Order error:", error);
alert("Network error while placing order.");
} finally {
setLoading(false);
}
@@ -171,11 +178,10 @@ const POS = () => {
<button
key={catName}
onClick={() => setActiveCategory(catName)}
className={`flex items-center gap-2 px-6 py-3 rounded-xl text-xs font-black transition-all border-2 whitespace-nowrap ${
activeCategory === catName
className={`flex items-center gap-2 px-6 py-3 rounded-xl text-xs font-black transition-all border-2 whitespace-nowrap ${activeCategory === catName
? 'bg-primary text-white border-primary shadow-lg shadow-primary/20'
: 'bg-white text-slate-400 border-indigo-50 hover:border-primary/30'
}`}
}`}
>
<Icon size={16} />
{catName}
@@ -189,9 +195,9 @@ const POS = () => {
<div className="flex-1 overflow-y-auto p-6 flex flex-col gap-6 custom-scrollbar">
<div className="relative group max-w-2xl">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-primary transition-colors" size={20} />
<input
type="text"
placeholder="Search Product..."
<input
type="text"
placeholder="Search Product..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-12 pr-4 py-4 bg-white border border-slate-200 rounded-2xl text-sm font-semibold outline-none focus:ring-4 focus:ring-primary/5 focus:border-primary transition-all shadow-sm"
@@ -204,8 +210,8 @@ const POS = () => {
</div>
) : filteredProducts.length === 0 ? (
<div className="flex-1 flex flex-col items-center justify-center text-slate-400">
<ShoppingBag size={64} className="mb-4 opacity-20" />
<p className="font-bold">No products found</p>
<ShoppingBag size={64} className="mb-4 opacity-20" />
<p className="font-bold">No products found</p>
</div>
) : (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6">
@@ -218,26 +224,25 @@ const POS = () => {
className="bg-white rounded-[2rem] p-5 border border-slate-100 shadow-sm hover:shadow-xl hover:border-primary/20 transition-all cursor-pointer group flex flex-col relative"
>
<div className="absolute top-4 right-4 z-10">
<span className={`px-2.5 py-1 rounded-lg text-[10px] font-black border uppercase tracking-wider ${
(product.stock || 0) < 20 ? 'bg-rose-50 text-rose-500 border-rose-100' : 'bg-emerald-50 text-emerald-500 border-emerald-100'
}`}>
{product.stock || 0} left
</span>
<span className={`px-2.5 py-1 rounded-lg text-[10px] font-black border uppercase tracking-wider ${(product.stock || 0) < 20 ? 'bg-rose-50 text-rose-500 border-rose-100' : 'bg-emerald-50 text-emerald-500 border-emerald-100'
}`}>
{product.stock || 0} left
</span>
</div>
<div className="aspect-square bg-slate-50 rounded-2xl mb-4 flex items-center justify-center text-slate-200 group-hover:bg-primary/5 transition-colors overflow-hidden">
{product.imageData ? (
<img src={product.imageData} alt={product.name} className="w-full h-full object-cover" />
) : (
<ShoppingBag size={48} />
)}
{product.imageData ? (
<img src={product.imageData} alt={product.name} className="w-full h-full object-cover" />
) : (
<ShoppingBag size={48} />
)}
</div>
<h3 className="text-xs font-black text-slate-800 leading-snug mb-1 line-clamp-2 uppercase">
{product.name}
</h3>
<div className="mt-auto">
<p className="text-lg font-black text-primary">{product.price}</p>
<p className="text-lg font-black text-primary">🅡{product.price}</p>
</div>
</motion.div>
))}
@@ -249,29 +254,29 @@ const POS = () => {
{/* Right Section: Current Order */}
<div className="w-[450px] bg-white border-l border-slate-200 flex flex-col shadow-[-10px_0_30px_rgba(0,0,0,0.02)]">
<div className="p-6 border-b border-slate-100 flex justify-between items-center">
<div>
<h2 className="text-xl font-black text-slate-900 tracking-tight">Current Order</h2>
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest flex items-center gap-1.5 mt-0.5">
<Phone size={10} className="text-primary" /> +91 9043941910
</p>
</div>
<div className="flex items-center gap-3">
<button className="px-4 py-2 bg-slate-50 text-slate-600 rounded-xl text-[10px] font-black uppercase hover:bg-slate-100 transition-colors">View Orders</button>
<span className="bg-primary text-white text-[10px] font-black px-3 py-1.5 rounded-lg uppercase shadow-lg shadow-primary/20">{cart.length} items</span>
</div>
<div>
<h2 className="text-xl font-black text-slate-900 tracking-tight">Current Order</h2>
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest flex items-center gap-1.5 mt-0.5">
<Phone size={10} className="text-primary" /> +91 9043941910
</p>
</div>
<div className="flex items-center gap-3">
<button className="px-4 py-2 bg-slate-50 text-slate-600 rounded-xl text-[10px] font-black uppercase hover:bg-slate-100 transition-colors">View Orders</button>
<span className="bg-primary text-white text-[10px] font-black px-3 py-1.5 rounded-lg uppercase shadow-lg shadow-primary/20">{cart.length} items</span>
</div>
</div>
<div className="flex-1 overflow-y-auto p-6 flex flex-col gap-4 custom-scrollbar">
<AnimatePresence mode="popLayout">
{cart.length === 0 ? (
<div className="flex-1 flex flex-col items-center justify-center text-center text-slate-400 gap-4">
<div className="w-20 h-20 bg-slate-50 rounded-3xl flex items-center justify-center">
<ShoppingBag size={40} className="text-slate-200" />
</div>
<div>
<h3 className="font-black text-slate-600">Cart is empty</h3>
<p className="text-xs font-medium">Select products to start billing</p>
</div>
<div className="w-20 h-20 bg-slate-50 rounded-3xl flex items-center justify-center">
<ShoppingBag size={40} className="text-slate-200" />
</div>
<div>
<h3 className="font-black text-slate-600">Cart is empty</h3>
<p className="text-xs font-medium">Select products to start billing</p>
</div>
</div>
) : (
cart.map((item) => (
@@ -286,9 +291,9 @@ const POS = () => {
<div className="flex justify-between items-start mb-4">
<div className="flex-1 pr-12">
<h4 className="text-[11px] font-black text-slate-800 uppercase tracking-tight leading-tight">{item.name}</h4>
<p className="text-[10px] font-bold text-[#42ab7e] mt-1 uppercase">{item.price} each</p>
<p className="text-[10px] font-bold text-[#42ab7e] mt-1 uppercase">🅡{item.price} each</p>
</div>
<button
<button
onClick={() => removeFromCart(item.id)}
className="w-9 h-9 bg-white text-rose-500 rounded-xl flex items-center justify-center shadow-sm border border-rose-50 hover:bg-rose-50 transition-colors"
>
@@ -298,14 +303,14 @@ const POS = () => {
<div className="flex justify-between items-center">
<div className="flex items-center bg-white rounded-xl p-1 shadow-sm border border-slate-50">
<button
<button
onClick={() => updateQuantity(item.id, -1)}
className="w-8 h-8 flex items-center justify-center text-slate-400 hover:text-primary transition-colors"
>
<Minus size={14} />
</button>
<span className="w-10 text-center font-black text-sm text-slate-900">{item.quantity}</span>
<button
<button
onClick={() => updateQuantity(item.id, 1)}
className="w-8 h-8 flex items-center justify-center text-slate-400 hover:text-primary transition-colors"
>
@@ -313,7 +318,7 @@ const POS = () => {
</button>
</div>
<div className="text-right">
<p className="text-lg font-black text-slate-900 tracking-tight">{item.price * item.quantity}</p>
<p className="text-lg font-black text-slate-900 tracking-tight">🅡{item.price * item.quantity}</p>
</div>
</div>
</motion.div>
@@ -323,22 +328,23 @@ const POS = () => {
</div>
<div className="p-8 bg-slate-50 border-t border-slate-200 shadow-[0_-10px_40px_rgba(0,0,0,0.03)] rounded-t-[3rem]">
<div className="flex justify-between items-center mb-6">
<span className="text-lg font-black text-slate-900">Total:</span>
<span className="text-4xl font-black text-primary tracking-tighter">{total}</span>
</div>
<button
<div className="flex justify-between items-center mb-6">
<span className="text-lg font-black text-slate-900">Total:</span>
<span className="text-4xl font-black text-primary tracking-tighter">🅡{total}</span>
</div>
<button
disabled={cart.length === 0}
onClick={handleCompleteOrder}
className="w-full bg-primary text-white py-5 rounded-[2rem] font-black text-sm uppercase tracking-[0.2em] shadow-2xl shadow-primary/20 hover:scale-[1.02] active:scale-[0.98] transition-all disabled:opacity-50 disabled:scale-100 disabled:shadow-none"
>
Complete Order
</button>
>
Complete Order
</button>
</div>
</div>
<style dangerouslySetInnerHTML={{ __html: `
<style dangerouslySetInnerHTML={{
__html: `
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.custom-scrollbar::-webkit-scrollbar { width: 5px; }

View File

@@ -1,20 +1,14 @@
import React, { useState, useEffect, useRef } from 'react';
import { useState, useEffect, useRef } from 'react';
import {
Plus,
X,
Search,
RefreshCw,
Edit2,
Trash2,
Database,
ShoppingBag,
Package,
Clock,
Check,
Power,
PowerOff,
Tag,
Barcode,
Image as ImageIcon
} from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
@@ -288,7 +282,7 @@ const Products = () => {
</div>
)}
<div className="absolute top-6 left-6 flex flex-col gap-2">
<span className="bg-white/90 backdrop-blur px-3 py-1 rounded-xl text-[9px] font-black uppercase text-primary border border-primary/10 shadow-sm">{prod.price}</span>
<span className="bg-white/90 backdrop-blur px-3 py-1 rounded-xl text-[9px] font-black uppercase text-primary border border-primary/10 shadow-sm">🅡{prod.price}</span>
</div>
<div className="absolute top-6 right-6 flex flex-col gap-2 opacity-0 group-hover:opacity-100 transition-all translate-x-4 group-hover:translate-x-0">
<button
@@ -384,7 +378,7 @@ const Products = () => {
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-4 mb-2 block">Pricing & Value</label>
<div className="grid grid-cols-2 gap-4">
<div className="relative">
<span className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-300 font-bold"></span>
<span className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-300 font-bold">🅡</span>
<input type="number" placeholder="MRP Price" value={formData.price} onChange={(e) => setFormData({...formData, price: parseFloat(e.target.value) || 0})} className="w-full bg-slate-50 border border-slate-100 rounded-2xl py-5 pl-10 pr-8 text-sm font-black outline-none focus:bg-white focus:ring-4 focus:ring-primary/5 focus:border-primary transition-all text-primary" />
</div>
<input type="number" placeholder="Inventory Count" value={formData.stock} onChange={(e) => setFormData({...formData, stock: parseInt(e.target.value) || 0})} className="bg-slate-50 border border-slate-100 rounded-2xl py-5 px-8 text-sm font-bold outline-none focus:bg-white focus:ring-4 focus:ring-primary/5 focus:border-primary transition-all" />