404 page added

This commit is contained in:
Sidharth Prabhu
2026-04-21 18:26:55 +05:30
parent 239c055fc4
commit 655fb251b9
11 changed files with 226 additions and 41 deletions

View File

@@ -18,6 +18,7 @@ import StallDetailScreen from './pages/StallDetailScreen';
import WalletScreen from './pages/WalletScreen';
import TopUpScreen from './pages/TopUpScreen';
import PreferencesScreen from './pages/PreferencesScreen';
import NotFoundScreen from './pages/NotFoundScreen';
import StockAlert from './components/StockAlert';
import './App.css';
@@ -98,6 +99,7 @@ function App() {
<PreferencesScreen />
</ProtectedRoute>
} />
<Route path="*" element={<NotFoundScreen />} />
</Routes>
</Router>
</CartProvider>

View File

@@ -10,6 +10,7 @@ interface AuthContextType {
logout: () => void;
changePin: (currentPin: string, newPin: string) => Promise<{ success: boolean; message: string }>;
updateProfile: (name: string, mobileNumber: string) => Promise<{ success: boolean; message: string }>;
refreshUser: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
@@ -137,35 +138,46 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
}
};
// Background suspension check
const refreshUser = async () => {
if (!user) return;
try {
const response = await fetch(`${API_BASE_URL}/user/${user.mobileNumber}`, { cache: 'no-store' });
if (response.ok) {
const data = await response.json();
const updatedUser: User = {
id: data.id,
name: data.name,
mobileNumber: data.mobileNumber,
isLoggedIn: data.isLoggedIn || data.loggedIn || true,
isSuspended: data.isSuspended || data.suspended,
ritzTokenBalance: data.ritzTokenBalance
};
// Update state and localStorage
setUser(updatedUser);
localStorage.setItem('user', JSON.stringify(updatedUser));
} else if (response.status === 404) {
logout();
}
} catch (error) {
console.error('Error refreshing user data:', error);
}
};
// Background status and balance sync
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 syncUserStatus = async () => {
await refreshUser();
};
const intervalId = setInterval(checkSuspensionStatus, 30000); // Check every 30 seconds
const intervalId = setInterval(syncUserStatus, 30000); // Sync every 30 seconds
return () => clearInterval(intervalId);
}, [user]);
return (
<AuthContext.Provider value={{ user, isLoading, checkUserExists, login, register, logout, changePin, updateProfile }}>
<AuthContext.Provider value={{ user, isLoading, checkUserExists, login, register, logout, changePin, updateProfile, refreshUser }}>
{children}
</AuthContext.Provider>
);

View File

@@ -1,19 +1,58 @@
.welcome-section {
padding: 24px 16px 8px;
background-color: var(--surface);
padding: 24px 20px 16px;
}
.greeting-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 4px;
}
.welcome-message {
font-size: 1.8rem;
font-weight: 800;
font-size: 26px;
font-weight: 850;
color: var(--text-dark);
line-height: 1.2;
margin: 0;
letter-spacing: -0.02em;
}
.wallet-badge {
display: flex;
align-items: center;
gap: 8px;
background: white;
padding: 8px 12px;
border-radius: 14px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.03);
cursor: pointer;
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.wallet-badge:active {
transform: scale(0.95);
}
.wallet-icon-img {
width: 22px;
height: 22px;
object-fit: contain;
}
.wallet-balance-text {
font-weight: 800;
font-size: 15px;
color: var(--primary-color);
letter-spacing: -0.01em;
}
.welcome-subtitle {
font-size: 0.95rem;
font-size: 15px;
color: var(--text-mid);
margin-top: 4px;
font-weight: 500;
margin: 0;
}
.search-bar-container {

View File

@@ -9,12 +9,13 @@ import FeedbackModal from '../components/FeedbackModal';
import { useFood } from '../contexts/FoodContext';
import { useAuth } from '../contexts/AuthContext';
import { motion, AnimatePresence } from 'framer-motion';
import walletIcon from '../assets/front.png';
import './HomeScreen.css';
const HomeScreen: React.FC = () => {
const navigate = useNavigate();
const { categories, stalls, foodItems, isLoading, error, refreshData } = useFood();
const { user } = useAuth();
const { user, refreshUser } = useAuth();
const [searchQuery, setSearchQuery] = useState('');
// Feedback state
@@ -25,6 +26,7 @@ const HomeScreen: React.FC = () => {
useEffect(() => {
if (user?.id) {
checkForUnratedOrder();
refreshUser();
}
}, [user]);
@@ -123,7 +125,15 @@ const HomeScreen: React.FC = () => {
<main className="safe-area-bottom">
<div className="welcome-section">
<h1 className="welcome-message">Hello {user?.name || 'Guest'}!</h1>
<div className="greeting-row">
<h1 className="welcome-message">Hello {user?.name || 'Guest'}!</h1>
{user && (
<div className="wallet-badge" onClick={() => navigate('/wallet')}>
<img src={walletIcon} alt="Wallet" className="wallet-icon-img" />
<span className="wallet-balance-text">R {user.ritzTokenBalance || 0}</span>
</div>
)}
</div>
<p className="welcome-subtitle">What would you like to eat today?</p>
</div>