Revenue functionality added and Removed the Expenses page
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user