import React from 'react'; import { Container, Typography, Paper, Box, Divider, Grid, Card, CardContent, List, ListItem, ListItemText, Chip, Avatar, Tabs, Tab, Stack, Button, Snackbar, Alert, Link as MuiLink } from '@mui/material'; import { Navigate, Link } from 'react-router-dom'; import PersonIcon from '@mui/icons-material/Person'; import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings'; import GroupIcon from '@mui/icons-material/Group'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import { ADMIN_COLORS, getAdminStyles } from '../theme/adminColors.js'; class UsersPage extends React.Component { constructor(props) { super(props); this.state = { user: null, users: [], totalCount: 0, totalOrders: 0, loading: true, redirect: false, switchingUser: false, notification: { open: false, message: '', severity: 'success' }, currentlyImpersonating: null }; } checkUserLoggedIn = () => { const storedUser = sessionStorage.getItem('user'); if (!storedUser) { this.setState({ redirect: true, user: null }); return; } try { const userData = JSON.parse(storedUser); if (!userData) { this.setState({ redirect: true, user: null }); } else if (!this.state.user) { // Only update user if it's not already set this.setState({ user: userData, loading: false }); } } catch (error) { console.error('Error parsing user from sessionStorage:', error); this.setState({ redirect: true, user: null }); } // Once loading is complete if (this.state.loading) { this.setState({ loading: false }); } } handleStorageChange = (e) => { if (e.key === 'user' && !e.newValue) { // User was removed from sessionStorage in another tab this.setState({ redirect: true, user: null }); } } componentDidMount() { this.loadInitialData(); this.checkUserLoggedIn(); // Set up interval to regularly check login status this.checkLoginInterval = setInterval(this.checkUserLoggedIn, 1000); // Add storage event listener to detect when user logs out in other tabs window.addEventListener('storage', this.handleStorageChange); } componentDidUpdate(prevProps) { // Handle socket connection changes const wasConnected = prevProps.socket && prevProps.socket.connected; const isNowConnected = this.props.socket && this.props.socket.connected; if (!wasConnected && isNowConnected) { // Socket just connected, reload data this.loadInitialData(); } } componentWillUnmount() { // Clear interval and remove event listeners if (this.checkLoginInterval) { clearInterval(this.checkLoginInterval); } window.removeEventListener('storage', this.handleStorageChange); } loadInitialData = () => { if (this.props.socket && this.props.socket.connected) { this.props.socket.emit('getUsers', (response) => { if (response.success) { console.log('Users:', response.data.users); console.log('Total count:', response.data.totalCount); console.log('Total orders:', response.data.totalOrders); this.setState({ users: response.data.users, totalCount: response.data.totalCount, totalOrders: response.data.totalOrders }); } else { console.error('Error:', response.error); } }); } } formatDate = (dateString) => { try { const date = new Date(dateString); // Check if date is valid if (isNaN(date.getTime())) { return dateString; // Return original string if date is invalid } return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (error) { console.error('Error formatting date:', error); return dateString; // Return original string if formatting fails } } getOrderStatusColor = (status) => { switch (status) { case 'completed': case 'delivered': return 'success'; case 'pending': return 'warning'; case 'processing': case 'shipped': return 'info'; case 'cancelled': return 'error'; default: return 'default'; } } getOrderStatusChipColor = (status) => { switch (status) { case 'completed': case 'delivered': return ADMIN_COLORS.primary; case 'pending': return ADMIN_COLORS.warning; case 'processing': case 'shipped': return ADMIN_COLORS.secondary; case 'cancelled': return ADMIN_COLORS.error; default: return ADMIN_COLORS.secondaryText; } } formatPrice = (price) => { return typeof price === 'number' ? `€${price.toFixed(2)}` : price; } handleSwitchUser = (email) => { if (!this.props.socket || !this.props.socket.connected) { this.showNotification('Socket not connected', 'error'); return; } this.setState({ switchingUser: true }); this.props.socket.emit('switchUser', { email }, (response) => { console.log('Switch user response:', response); this.setState({ switchingUser: false }); if (response.success) { this.setState({ currentlyImpersonating: response.data.targetUser }); this.showNotification(`Successfully switched to user: ${email}`, 'success'); // Update sessionStorage with the switched user info const currentUser = JSON.parse(sessionStorage.getItem('user') || '{}'); const switchedUser = { ...currentUser, id: response.data.targetUser.id, email: response.data.targetUser.email, admin: true, // Admin privileges are preserved originalAdmin: response.data.originalAdmin }; sessionStorage.setItem('user', JSON.stringify(switchedUser)); // Trigger userLoggedIn event to refresh other components window.dispatchEvent(new Event('userLoggedIn')); } else { this.showNotification(`Failed to switch user: ${response.error}`, 'error'); } }); } handleSwitchBackToAdmin = () => { if (!this.props.socket || !this.props.socket.connected) { this.showNotification('Socket not connected', 'error'); return; } this.setState({ switchingUser: true }); this.props.socket.emit('switchBackToAdmin', (response) => { console.log('Switch back to admin response:', response); this.setState({ switchingUser: false }); if (response.success) { this.setState({ currentlyImpersonating: null }); this.showNotification(`Switched back to admin`, 'success'); // Restore original admin info in sessionStorage const currentUser = JSON.parse(sessionStorage.getItem('user') || '{}'); if (currentUser.originalAdmin) { const restoredAdmin = { ...currentUser, id: currentUser.originalAdmin.id, email: currentUser.originalAdmin.email, admin: true }; delete restoredAdmin.originalAdmin; sessionStorage.setItem('user', JSON.stringify(restoredAdmin)); } // Trigger userLoggedIn event to refresh other components window.dispatchEvent(new Event('userLoggedIn')); } else { this.showNotification(`Failed to switch back: ${response.error}`, 'error'); } }); } showNotification = (message, severity = 'success') => { console.log('Showing notification:', message, severity); this.setState({ notification: { open: true, message, severity } }); } handleCloseNotification = () => { this.setState({ notification: { ...this.state.notification, open: false } }); } render() { const { users, totalCount, totalOrders } = this.state; if (this.state.redirect || (!this.state.loading && !this.state.user)) { return ; } // Check if current user is admin if (this.state.user && !this.state.user.admin) { return ; } const hasUsers = users && users.length > 0; const styles = getAdminStyles(); return ( {/* Admin Navigation Tabs */} User Management {this.state.currentlyImpersonating && ( <> )} Total Users: {totalCount} Total Orders: {totalOrders} {!hasUsers && ( No users found. )} {hasUsers && ( {users.map((user, i) => ( {user.admin ? : } User #{user.id} this.handleSwitchUser(user.email)} disabled={this.state.switchingUser} sx={{ color: ADMIN_COLORS.secondary, textDecoration: 'underline', textDecorationColor: 'transparent', cursor: 'pointer', fontSize: '0.875rem', fontWeight: 'medium', border: 'none', background: 'none', padding: 0, textAlign: 'left', display: 'block', width: '100%', textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', fontFamily: ADMIN_COLORS.fontFamily, '&:hover': { textDecorationColor: ADMIN_COLORS.secondary, color: ADMIN_COLORS.primaryBright }, '&:disabled': { color: ADMIN_COLORS.secondaryText, cursor: 'not-allowed' } }} title="Click to switch to this user" > {user.email} {user.admin == true&& ( )} {/* All Orders */} {user.orders && user.orders.length > 0 && ( <> Orders {user.orders .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)) // Sort by newest first .map((order, orderIndex) => ( {order.orderId || 'N/A'} } secondary={ {this.formatDate(order.created_at)} {order.totalCost ? this.formatPrice(order.totalCost) : 'N/A'} } /> ))} )} ))} )} {/* Notification Snackbar */} {this.state.notification.message} ); } } export default UsersPage;