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

@@ -117,12 +117,12 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
if (response.ok) {
const updatedUserDto = await response.json();
// The DTO from backend might have different structure, let's map it back to User type
const updatedUser: User = {
id: updatedUserDto.id,
name: updatedUserDto.name,
mobileNumber: updatedUserDto.mobileNumber,
isLoggedIn: updatedUserDto.loggedIn,
isSuspended: updatedUserDto.suspended,
ritzTokenBalance: updatedUserDto.ritzTokenBalance
};
setUser(updatedUser);
@@ -137,6 +137,33 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
}
};
// Background suspension check
useEffect(() => {
if (!user) return;
const checkSuspensionStatus = async () => {
try {
const response = await fetch(`${API_BASE_URL}/user/${user.mobileNumber}`, { cache: 'no-store' });
if (response.status === 404) {
logout(); // User deleted
return;
}
if (response.ok) {
const data = await response.json();
if (data.isSuspended || data.suspended) {
console.log("Account suspended. Logging out...");
logout();
}
}
} catch (error) {
console.error('Error checking user status:', error);
}
};
const intervalId = setInterval(checkSuspensionStatus, 30000); // Check every 30 seconds
return () => clearInterval(intervalId);
}, [user]);
return (
<AuthContext.Provider value={{ user, isLoading, checkUserExists, login, register, logout, changePin, updateProfile }}>
{children}

View File

@@ -152,6 +152,124 @@
color: var(--primary);
}
.error-message {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.2);
color: #ef4444;
padding: 12px 16px;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
margin-bottom: 24px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
/* Suspension Modal Styles */
.suspension-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(8px);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.suspension-modal {
background: rgba(255, 255, 255, 0.95);
width: 100%;
max-width: 400px;
border-radius: 28px;
padding: 40px 32px;
text-align: center;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.suspension-icon {
width: 80px;
height: 80px;
background: #fef2f2;
color: #ef4444;
border-radius: 24px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.suspension-modal h2 {
font-size: 24px;
font-weight: 800;
color: #1e293b;
margin-bottom: 12px;
}
.suspension-modal p {
color: #64748b;
font-size: 15px;
line-height: 1.6;
margin-bottom: 24px;
}
.suspension-details {
background: #f8fafc;
padding: 16px;
border-radius: 16px;
margin-bottom: 32px;
border: 1px solid #e2e8f0;
}
.suspension-details span {
color: #475569;
font-size: 13px;
font-weight: 600;
line-height: 1.5;
display: block;
}
.suspension-close-btn {
width: 100%;
padding: 16px;
background: #231651;
color: white;
border: none;
border-radius: 16px;
font-size: 16px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.suspension-close-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px -5px rgba(35, 22, 81, 0.4);
}
.suspension-close-btn:active {
transform: translateY(0);
}
@keyframes zoomIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
.animate-zoomIn {
animation: zoomIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.login-button {
background-color: var(--primary);
color: white;

View File

@@ -15,6 +15,7 @@ const LoginScreen: React.FC = () => {
const [pin, setPin] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isSuspended, setIsSuspended] = useState(false);
const handleMobileSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -53,6 +54,8 @@ const LoginScreen: React.FC = () => {
if (result.success) {
navigate('/');
} else if (result.isSuspended) {
setIsSuspended(true);
} else {
setError(result.message);
}
@@ -215,6 +218,24 @@ const LoginScreen: React.FC = () => {
<p>RIT Canteen Management System</p>
</div>
</div>
{isSuspended && (
<div className="suspension-overlay">
<div className="suspension-modal animate-zoomIn">
<div className="suspension-icon">
<Lock size={40} />
</div>
<h2>Account Suspended</h2>
<p>Your access to the RIT Canteen system has been restricted by an administrator.</p>
<div className="suspension-details">
<span>Please contact the Admin or Manager to resolve this issue.</span>
</div>
<button className="suspension-close-btn" onClick={() => setIsSuspended(false)}>
Back to Login
</button>
</div>
</div>
)}
</div>
);
};

View File

@@ -33,6 +33,7 @@ export interface User {
mobileNumber: string;
name: string;
isLoggedIn: boolean;
isSuspended: boolean;
ritzTokenBalance: number;
}