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, Tabs, Tab, 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 PublicIcon from '@mui/icons-material/Public'; import ShieldIcon from '@mui/icons-material/Shield'; import GppBadIcon from '@mui/icons-material/GppBad'; import PeopleIcon from '@mui/icons-material/People'; 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, addAccount, deleteAccount, switchAccount, openSteamLogin, updateServerConfig, loginToServer, getCommunityAccounts, syncNow } = useAccounts(); const [searchTerm, setSearchTerm] = useState(''); const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [identifier, setIdentifier] = useState(''); const [addTab, setAddTab] = useState(0); const [communityAccounts, setCommunityAccounts] = useState([]); const [isCommunityLoading, setIsCommunityLoading] = useState(false); const [serverUrl, setServerUrl] = useState(''); useEffect(() => { if (serverConfig?.url) { setServerUrl(serverConfig.url); } }, [serverConfig?.url]); const loadCommunity = async () => { setIsCommunityLoading(true); try { const data = await getCommunityAccounts(); setCommunityAccounts(Array.isArray(data) ? data : []); } catch (e) { } finally { setIsCommunityLoading(false); } }; useEffect(() => { if (isAddDialogOpen && addTab === 1) { loadCommunity(); } }, [isAddDialogOpen, addTab]); const handleAddAccount = async () => { if (!identifier) return; try { await addAccount({ identifier }); setIsAddDialogOpen(false); setIdentifier(''); } catch (e) { console.error("[Dashboard] Add failed:", e); } }; const handleAddFromCommunity = async (commAcc: any) => { try { await addAccount({ identifier: commAcc.steamId }); setIsAddDialogOpen(false); } catch (e) { } }; 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 Account" 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 }} /> {/* Add Account Dialog */} setIsAddDialogOpen(false)} maxWidth="sm" fullWidth> setAddTab(v)} variant="fullWidth" textColor="inherit" indicatorColor="primary"> } iconPosition="start" /> } iconPosition="start" disabled={!serverConfig?.token} /> {addTab === 0 ? ( <> Enter a SteamID64 or Profile URL. You will need to authenticate to enable full tracking and instant login features. setIdentifier(e.target.value)} sx={{ '& .MuiOutlinedInput-root': { backgroundColor: 'rgba(0, 0, 0, 0.1)' } }} /> ) : ( {isCommunityLoading ? ( ) : ( {communityAccounts .filter(ca => !safeAccounts.find(a => a.steamId === ca.steamId)) .map((ca) => ( ))} {communityAccounts.length === 0 && No shared accounts found on server.} )} )} {addTab === 0 && }
); }; // --- 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, 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); alert(`Account shared successfully!`); setIsShareOpen(false); setTargetUserId(''); } catch (e: any) { alert(e.message || "Failed to share account"); } finally { setIsSharing(false); } }; const isBanned = account?.vacBanned || (account?.gameBans && account.gameBans > 0); const isShared = account?._id.startsWith('shared_'); return ( {isShared && ( )} {account?.personaName || 'Unknown'} {account?.steamId} {isBanned ? ( ACCOUNT BANNED {account?.vacBanned && ( )} {account?.gameBans ? account.gameBans > 0 && ( ) : null} ) : ( SECURE )} {account?.authError ? ( Needs Re-auth ) : isCooldownActive ? ( {timeLeft} ) : ( Available )} {account?.steamLoginSecure ? ( ) : ( )} (window as any).electronAPI.openExternal(account?.profileUrl || '')}> onDelete(account?._id || '')}> {/* Share Dialog */} setIsShareOpen(false)} maxWidth="xs" fullWidth> Share Account Select a community member to share this account with. Select User ); }; export default Dashboard;