import React, { useState, useEffect } from 'react'; import { Box, Container, Typography, Button, TextField, InputAdornment, IconButton, AppBar, Toolbar, Avatar, Tooltip, Dialog, DialogTitle, DialogContent, DialogActions, CircularProgress, Paper, Chip, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Switch, FormControlLabel, Divider, List, ListItem, ListItemText, ListItemSecondaryAction, Select, MenuItem, FormControl, InputLabel } from '@mui/material'; import SearchIcon from '@mui/icons-material/Search'; import AddIcon from '@mui/icons-material/Add'; import SteamIcon from '@mui/icons-material/SportsEsports'; import DeleteIcon from '@mui/icons-material/Delete'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import SyncIcon from '@mui/icons-material/Sync'; import BoltIcon from '@mui/icons-material/Bolt'; import TimerIcon from '@mui/icons-material/Timer'; import LockResetIcon from '@mui/icons-material/LockReset'; import SettingsIcon from '@mui/icons-material/Settings'; import ShareIcon from '@mui/icons-material/Share'; import GroupAddIcon from '@mui/icons-material/GroupAdd'; import ShieldIcon from '@mui/icons-material/Shield'; import GppBadIcon from '@mui/icons-material/GppBad'; import PeopleIcon from '@mui/icons-material/People'; import VerifiedUserIcon from '@mui/icons-material/VerifiedUser'; import WorkspacePremiumIcon from '@mui/icons-material/WorkspacePremium'; import { useAccounts, type Account } from '../hooks/useAccounts'; import { useAppTheme } from '../theme/ThemeContext'; import type { ThemeType } from '../theme/SteamTheme'; import NebulaBanner from '../components/NebulaBanner'; const Dashboard: React.FC = () => { const { currentTheme, setTheme } = useAppTheme(); const { accounts, isLoading, isSyncing, serverConfig, deleteAccount, switchAccount, openSteamAppLogin, openSteamLogin, updateServerConfig, loginToServer, syncNow } = useAccounts(); const [searchTerm, setSearchTerm] = useState(''); const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [serverUrl, setServerUrl] = useState(''); useEffect(() => { if (serverConfig?.url) setServerUrl(serverConfig.url); }, [serverConfig?.url]); const saveSettings = async () => { await updateServerConfig({ url: serverUrl }); alert("Server URL updated!"); }; const safeAccounts = Array.isArray(accounts) ? accounts : []; const filteredAccounts = safeAccounts.filter((acc) => { if (!acc) return false; const name = acc.personaName || 'Unknown'; const id = acc.steamId || ''; return name.toLowerCase().includes(searchTerm.toLowerCase()) || id.includes(searchTerm); }); return ( ULTIMATE BAN TRACKER {isSyncing ? ( ) : ( syncNow()} disabled={!serverConfig?.enabled} sx={{ color: 'primary.main' }}> )} setSearchTerm(e.target.value)} sx={{ backgroundColor: 'rgba(255, 255, 255, 0.05)', borderRadius: 1, width: 200, '& .MuiOutlinedInput-root': { '& fieldset': { border: 'none' }, height: 32 } }} InputProps={{ startAdornment: ( ), }} /> setIsSettingsOpen(true)}> {isLoading ? ( ) : ( AVATAR ACCOUNT BAN STATUS COOLDOWN ACTIONS {filteredAccounts.map((account) => ( openSteamLogin(account.steamId)} /> ))}
)} {!isLoading && filteredAccounts.length === 0 && ( No accounts tracked. Click "Add" to get started! )}
{/* Settings Dialog */} setIsSettingsOpen(false)} maxWidth="sm" fullWidth> Settings & Customization THEME SELECTION Active Theme BACKEND CONFIGURATION setServerUrl(e.target.value)} placeholder="https://ultimate-ban-tracker.narl.io" margin="dense" sx={{ mb: 2 }} InputProps={{ endAdornment: ( ), }} /> COMMUNITY AUTHENTICATION {serverConfig?.token ? "Connected to Server" : "Not Authenticated"} {serverConfig?.token ? "Your accounts can now be shared with others." : "Login to share and sync with your community."} {serverConfig?.token && ( )} updateServerConfig({ enabled: e.target.checked })} disabled={!serverConfig?.token} /> } label="Enable Community Sync" sx={{ mt: 2 }} />
); }; // --- Sub-Component: AccountRow --- const AccountRow: React.FC<{ account: Account, onDelete: (id: string) => void, onSwitch: (login: string) => void, onAuth: () => void }> = ({ account, onDelete, onSwitch, onAuth }) => { const { shareAccountWithUser, revokeAccountAccess, revokeAllAccountAccess, getServerUsers, serverConfig } = useAccounts(); const [timeLeft, setTimeLeft] = useState(null); const [isShareOpen, setIsShareOpen] = useState(false); const [targetUserId, setTargetUserId] = useState(''); const [isSharing, setIsSharing] = useState(false); const [serverUsers, setServerUsers] = useState([]); const cooldownDate = account?.cooldownExpiresAt ? new Date(account.cooldownExpiresAt) : null; const isCooldownActive = cooldownDate && !isNaN(cooldownDate.getTime()) && cooldownDate.getTime() > Date.now(); useEffect(() => { if (!isCooldownActive || !cooldownDate) { setTimeLeft(null); return; } const targetTime = cooldownDate.getTime(); const timer = setInterval(() => { const diff = targetTime - Date.now(); if (diff <= 0) { setTimeLeft(null); clearInterval(timer); return; } const hours = Math.floor(diff / 3600000); const mins = Math.floor((diff % 3600000) / 60000); const secs = Math.floor((diff % 60000) / 1000); setTimeLeft(`${hours}h ${mins}m ${secs}s`); }, 1000); return () => clearInterval(timer); }, [account?.cooldownExpiresAt, isCooldownActive]); const avatarSrc = account?.localAvatar ? `steam-resource://${account.localAvatar}` : (account?.avatar || ''); const [imgSrc, setImgSrc] = useState(avatarSrc); useEffect(() => { setImgSrc(avatarSrc); }, [avatarSrc]); const handleOpenShare = async () => { setIsShareOpen(true); try { const [users, selfInfo] = await Promise.all([ getServerUsers(), (window as any).electronAPI.getServerUserInfo() ]); const filtered = (Array.isArray(users) ? users : []).filter(u => u.steamId !== selfInfo.steamId && u.steamId !== account.steamId ); setServerUsers(filtered); } catch (e) {} }; const handleShare = async () => { if (!targetUserId) return; setIsSharing(true); try { await shareAccountWithUser(account.steamId, targetUserId); setTargetUserId(''); } catch (e: any) { alert(e.message || "Failed to share account"); } finally { setIsSharing(false); } }; const handleRevoke = async (targetSteamId: string) => { if (!window.confirm("Revoke access for this user?")) return; try { await revokeAccountAccess(account.steamId, targetSteamId); } catch (e: any) { alert(e.message); } }; const handleRevokeAll = async () => { if (!window.confirm("Completely stop sharing this account?")) return; try { await revokeAllAccountAccess(account.steamId); setIsShareOpen(false); } catch (e: any) { alert(e.message); } }; const isBanned = account?.vacBanned || (account?.gameBans && account.gameBans > 0); // Primary account check const isPrimaryAccount = serverConfig?.serverSteamId === account.steamId; // Refined Shared Logic const isSharedWithYou = account?._id.startsWith('shared_'); const hasSharedMembers = (account as any).sharedWith && (account as any).sharedWith.length > 0; const showCommunityIcon = isSharedWithYou || hasSharedMembers; return ( {isPrimaryAccount && ( )} {showCommunityIcon && ( )} {account?.personaName || 'Unknown'} {account?.steamId} {isBanned ? ( BANNED {account?.vacBanned && } {account?.gameBans ? account.gameBans > 0 && : null} ) : ( SECURE )} {account?.authError ? ( Needs Re-auth ) : isCooldownActive ? ( {timeLeft} ) : ( Available )} {account.loginName && ( )} {account.steamLoginSecure && !account.authError ? : (account.authError ? : )} {account.steamLoginSecure && !account.authError && ( TRACKING )} (window as any).electronAPI.openExternal(account?.profileUrl || '')}> onDelete(account?._id || '')}> setIsShareOpen(false)} maxWidth="xs" fullWidth> Permissions GRANT ACCESS Select User CURRENT ACCESS {(account as any).sharedWith?.map((sw: any) => ( handleRevoke(sw.steamId)}> ))} {(!(account as any).sharedWith || (account as any).sharedWith.length === 0) && ( Not shared with anyone yet. )} {(account as any).sharedWith?.length > 0 && ( )} ); }; export default Dashboard;