User suspension added

This commit is contained in:
Sidharth Prabhu
2026-04-21 14:37:34 +05:30
parent 21c581b7eb
commit b4bf6ebeb3
13 changed files with 301 additions and 24 deletions

View File

@@ -7,6 +7,7 @@ interface UserDto {
mobileNumber: string;
name: string;
loggedIn: boolean;
isSuspended: boolean;
ritzTokenBalance: number;
}
@@ -66,7 +67,13 @@ const Customers: React.FC = () => {
if (response.ok) {
const data = await response.json();
if (data && data.content) {
setUsers(data.content);
// Normalize the data (backend sends 'suspended', frontend expects 'isSuspended')
const normalizedUsers = data.content.map((u: any) => ({
...u,
isSuspended: u.isSuspended !== undefined ? u.isSuspended : u.suspended,
loggedIn: u.loggedIn !== undefined ? u.loggedIn : u.isLoggedIn
}));
setUsers(normalizedUsers);
setTotalElements(data.totalElements);
} else {
setUsers([]);
@@ -115,6 +122,25 @@ const Customers: React.FC = () => {
}
};
const handleSuspendToggle = async (user: UserDto) => {
try {
const host = window.location.hostname;
const response = await fetch(`http://${host}:8080/api/auth/users/${user.id}/suspend`, {
method: 'PATCH',
});
if (response.ok) {
showNotification(`${user.name} ${user.isSuspended ? 'unsuspended' : 'suspended'} successfully`, 'success');
fetchUsers();
setOpenMenuId(null);
} else {
showNotification('Failed to update suspension status', 'error');
}
} catch (error) {
console.error('Error toggling suspension:', error);
showNotification('Error connecting to server', 'error');
}
};
const validateForm = () => {
const newErrors: {[key: string]: string} = {};
if (!editForm.name.trim()) newErrors.name = 'Full Name is required';
@@ -283,19 +309,16 @@ const Customers: React.FC = () => {
</span>
</div>
</td>
<td className="px-6 py-4">
<span className="text-sm font-bold text-[#231651]">
{user.ritzTokenBalance?.toLocaleString() || '0'}
</span>
</td>
<td className="px-6 py-4">
<span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-bold ${
user.loggedIn
? 'bg-green-100 text-green-700'
: 'bg-gray-100 text-gray-600'
user.isSuspended
? 'bg-red-100 text-red-700'
: user.loggedIn
? 'bg-green-100 text-green-700'
: 'bg-gray-100 text-gray-600'
}`}>
<div className={`w-1.5 h-1.5 rounded-full ${user.loggedIn ? 'bg-green-500' : 'bg-gray-400'}`} />
{user.loggedIn ? 'Active Now' : 'Offline'}
<div className={`w-1.5 h-1.5 rounded-full ${user.isSuspended ? 'bg-red-500' : user.loggedIn ? 'bg-green-500' : 'bg-gray-400'}`} />
{user.isSuspended ? 'Suspended' : user.loggedIn ? 'Active Now' : 'Offline'}
</span>
</td>
<td className="px-6 py-4 text-right relative">
@@ -308,6 +331,10 @@ const Customers: React.FC = () => {
<button onClick={() => handleEditClick(user)} className="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-50 text-[#475569] font-medium border-b border-[#f1f5f9]">
<Edit2 size={16} /> Edit User
</button>
<button onClick={() => handleSuspendToggle(user)} className={`w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-50 font-medium border-b border-[#f1f5f9] ${user.isSuspended ? 'text-amber-600' : 'text-red-500'}`}>
{user.isSuspended ? <UserCheck size={16} /> : <UserMinus size={16} />}
{user.isSuspended ? 'Unsuspend User' : 'Suspend User'}
</button>
<button onClick={() => handleDeleteClick(user)} className="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-50 text-red-600 font-medium font-bold">
<Trash2 size={16} /> Delete User
</button>
@@ -339,9 +366,13 @@ const Customers: React.FC = () => {
</button>
{openMenuId === user.id && (
<div className="absolute right-0 mt-2 w-40 bg-white rounded-xl shadow-2xl border border-[#e2e8f0] z-50 overflow-hidden">
<button onClick={() => handleEditClick(user)} className="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-50 text-[#475569] font-medium">
<button onClick={() => handleEditClick(user)} className="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-50 text-[#475569] font-medium border-b border-[#f1f5f9]">
<Edit2 size={16} /> Edit
</button>
<button onClick={() => handleSuspendToggle(user)} className={`w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-50 font-medium border-b border-[#f1f5f9] ${user.isSuspended ? 'text-amber-600' : 'text-red-500'}`}>
{user.isSuspended ? <UserCheck size={16} /> : <UserMinus size={16} />}
{user.isSuspended ? 'Unsuspend' : 'Suspend'}
</button>
<button onClick={() => handleDeleteClick(user)} className="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-50 text-red-600 font-medium">
<Trash2 size={16} /> Delete
</button>
@@ -363,6 +394,11 @@ const Customers: React.FC = () => {
<span className="px-2 py-0.5 bg-blue-50 text-blue-600 text-[10px] font-bold rounded-lg border border-blue-100 uppercase tracking-wider">
RIT STUDENT
</span>
{user.isSuspended && (
<span className="px-2 py-0.5 bg-red-50 text-red-600 text-[10px] font-bold rounded-lg border border-red-100 uppercase tracking-wider">
SUSPENDED
</span>
)}
<span className="px-2 py-0.5 bg-indigo-50 text-indigo-600 text-[10px] font-bold rounded-lg border border-indigo-100 uppercase tracking-wider">
{user.ritzTokenBalance?.toLocaleString() || '0'} TOKENS
</span>

View File

@@ -5,7 +5,8 @@ import {
Wallet,
ArrowRight,
RotateCcw,
Star
Star,
UserMinus
} from 'lucide-react';
import {
AreaChart,
@@ -29,7 +30,8 @@ const StoreDashboard = () => {
totalSales: 0,
activeOrders: 0,
dailyCustomers: 0,
revenueGrowth: 0
revenueGrowth: 0,
suspendedUserCount: 0
});
const [trendingItems, setTrendingItems] = useState<any[]>([]);
const [salesData, setSalesData] = useState<any[]>([]);
@@ -107,7 +109,8 @@ const StoreDashboard = () => {
totalSales: data.stats.totalSales,
activeOrders: data.stats.activeOrders,
dailyCustomers: data.stats.dailyCustomers,
revenueGrowth: data.stats.growth
revenueGrowth: data.stats.growth,
suspendedUserCount: data.stats.suspendedUserCount
});
}
@@ -290,8 +293,21 @@ const StoreDashboard = () => {
</div>
</div>
{/* Restricted Accounts Card */}
<div title="Monitor suspended customer accounts" className="bg-white p-6 rounded-[24px] border border-slate-100 shadow-sm h-[130px] flex flex-col justify-between group cursor-pointer hover:border-red-500/30 transition-all">
<div className="flex items-center gap-3">
<div className="p-2 bg-red-50 text-red-500 rounded-xl group-hover:scale-110 transition-transform">
<UserMinus size={20} />
</div>
<h4 className="text-[11px] font-black text-slate-400 uppercase tracking-widest leading-none">Restricted Accounts</h4>
</div>
<div className="text-center">
<h2 className="text-4xl font-black text-slate-800 tracking-tighter">{stats?.suspendedUserCount || 0}</h2>
</div>
</div>
{/* Expenses Card */}
<div title="Monitor store operational expenditures" className="bg-white p-6 rounded-[24px] border border-slate-100 shadow-sm h-[150px] flex flex-col justify-between group cursor-pointer hover:border-[#0f4475]/30 transition-all">
<div title="Monitor store operational expenditures" className="bg-white p-6 rounded-[24px] border border-slate-100 shadow-sm h-[130px] flex flex-col justify-between group cursor-pointer hover:border-[#0f4475]/30 transition-all">
<div className="flex items-center gap-3">
<div className="p-2 bg-amber-100 text-[#0f4475] rounded-xl shadow-sm">
<Wallet size={20} />