Added user account overview for admins

This commit is contained in:
2026-01-14 22:36:59 +06:00
parent 1eaba59f0f
commit 14f020e819
5 changed files with 318 additions and 47 deletions

View File

@@ -25,6 +25,7 @@ function App() {
const [showUsers, setShowUsers] = useState(false);
const [showTickets, setShowTickets] = useState(false);
const [showProfile, setShowProfile] = useState(false);
const [viewingUsername, setViewingUsername] = useState(null);
const [connectionError, setConnectionError] = useState(false);
const [theme, setTheme] = useState(localStorage.getItem('theme') || 'dark');
const [sidebarOpen, setSidebarOpen] = useState(true);
@@ -118,6 +119,12 @@ function App() {
loadServers();
};
const handleViewProfile = (username) => {
setViewingUsername(username);
setShowProfile(true);
setShowUsers(false);
};
const startServer = async (serverName) => {
try {
const response = await axios.post(
@@ -201,7 +208,7 @@ function App() {
</div>
</div>
</header>
<Users token={token} theme={currentTheme} />
<Users token={token} theme={currentTheme} onViewProfile={handleViewProfile} />
</div>
);
}
@@ -284,7 +291,10 @@ function App() {
</div>
<ThemeSelector currentTheme={theme} onThemeChange={handleThemeChange} />
<button
onClick={() => setShowProfile(false)}
onClick={() => {
setShowProfile(false);
setViewingUsername(null);
}}
className={`${currentTheme.card} ${currentTheme.hover} px-4 py-2 rounded-lg transition flex items-center gap-2`}
>
<Server className="w-4 h-4" />
@@ -301,7 +311,7 @@ function App() {
</div>
</div>
</header>
<Profile token={token} user={user} theme={currentTheme} onUsernameChange={handleUsernameChange} />
<Profile token={token} user={user} theme={currentTheme} onUsernameChange={handleUsernameChange} viewingUsername={viewingUsername} />
</div>
);
}
@@ -345,7 +355,10 @@ function App() {
</div>
<ThemeSelector currentTheme={theme} onThemeChange={handleThemeChange} />
<button
onClick={() => setShowProfile(true)}
onClick={() => {
setShowProfile(true);
setViewingUsername(null);
}}
className={`${currentTheme.accent} ${currentTheme.accentHover} px-4 py-2 rounded-lg transition flex items-center gap-2 text-white`}
>
<UserCircle className="w-4 h-4" />

View File

@@ -3,10 +3,11 @@ import { User, Lock, Server, MessageSquare, Shield, TrendingUp, Eye, EyeOff } fr
import axios from 'axios';
import { API_URL } from '../config';
export default function Profile({ token, user, theme, onUsernameChange }) {
export default function Profile({ token, user, theme, onUsernameChange, viewingUsername }) {
const [stats, setStats] = useState(null);
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState('overview');
const isViewingOther = viewingUsername && viewingUsername !== user?.username;
// Форма смены имени
const [usernameForm, setUsernameForm] = useState({
@@ -28,11 +29,15 @@ export default function Profile({ token, user, theme, onUsernameChange }) {
useEffect(() => {
loadStats();
}, []);
}, [viewingUsername]);
const loadStats = async () => {
try {
const { data } = await axios.get(`${API_URL}/api/profile/stats`, {
const endpoint = isViewingOther
? `${API_URL}/api/profile/stats/${viewingUsername}`
: `${API_URL}/api/profile/stats`;
const { data } = await axios.get(endpoint, {
headers: { Authorization: `Bearer ${token}` }
});
setStats(data);
@@ -157,49 +162,55 @@ export default function Profile({ token, user, theme, onUsernameChange }) {
<div className="max-w-6xl mx-auto">
{/* Header */}
<div className="mb-6">
<h1 className="text-2xl font-bold mb-2">Личный кабинет</h1>
<p className={theme.textSecondary}>Управление профилем и настройками</p>
<h1 className="text-2xl font-bold mb-2">
{isViewingOther ? `Профиль пользователя: ${viewingUsername}` : 'Личный кабинет'}
</h1>
<p className={theme.textSecondary}>
{isViewingOther ? 'Просмотр профиля другого пользователя' : 'Управление профилем и настройками'}
</p>
</div>
{/* Tabs */}
<div className={`${theme.secondary} ${theme.border} border rounded-2xl mb-6 p-2 flex gap-2`}>
<button
onClick={() => setActiveTab('overview')}
className={`flex-1 px-4 py-3 rounded-xl font-medium transition ${
activeTab === 'overview'
? `${theme.accent} text-white`
: `${theme.hover}`
}`}
>
<TrendingUp className="w-4 h-4 inline mr-2" />
Обзор
</button>
<button
onClick={() => setActiveTab('username')}
className={`flex-1 px-4 py-3 rounded-xl font-medium transition ${
activeTab === 'username'
? `${theme.accent} text-white`
: `${theme.hover}`
}`}
>
<User className="w-4 h-4 inline mr-2" />
Имя пользователя
</button>
<button
onClick={() => setActiveTab('password')}
className={`flex-1 px-4 py-3 rounded-xl font-medium transition ${
activeTab === 'password'
? `${theme.accent} text-white`
: `${theme.hover}`
}`}
>
<Lock className="w-4 h-4 inline mr-2" />
Пароль
</button>
</div>
{!isViewingOther && (
<div className={`${theme.secondary} ${theme.border} border rounded-2xl mb-6 p-2 flex gap-2`}>
<button
onClick={() => setActiveTab('overview')}
className={`flex-1 px-4 py-3 rounded-xl font-medium transition ${
activeTab === 'overview'
? `${theme.accent} text-white`
: `${theme.hover}`
}`}
>
<TrendingUp className="w-4 h-4 inline mr-2" />
Обзор
</button>
<button
onClick={() => setActiveTab('username')}
className={`flex-1 px-4 py-3 rounded-xl font-medium transition ${
activeTab === 'username'
? `${theme.accent} text-white`
: `${theme.hover}`
}`}
>
<User className="w-4 h-4 inline mr-2" />
Имя пользователя
</button>
<button
onClick={() => setActiveTab('password')}
className={`flex-1 px-4 py-3 rounded-xl font-medium transition ${
activeTab === 'password'
? `${theme.accent} text-white`
: `${theme.hover}`
}`}
>
<Lock className="w-4 h-4 inline mr-2" />
Пароль
</button>
</div>
)}
{/* Overview Tab */}
{activeTab === 'overview' && (
{(activeTab === 'overview' || isViewingOther) && (
<div className="space-y-6">
{/* User Info Card */}
<div className={`${theme.card} ${theme.border} border rounded-2xl p-6`}>

View File

@@ -3,7 +3,7 @@ import { Users as UsersIcon, Trash2, Shield, User } from 'lucide-react';
import axios from 'axios';
import { API_URL } from '../config';
export default function Users({ token }) {
export default function Users({ token, onViewProfile }) {
const [users, setUsers] = useState([]);
const [servers, setServers] = useState([]);
const [loading, setLoading] = useState(true);
@@ -111,7 +111,13 @@ export default function Users({ token }) {
)}
</div>
<div>
<h3 className="text-lg font-semibold">{user.username}</h3>
<button
onClick={() => onViewProfile && onViewProfile(user.username)}
className="text-lg font-semibold hover:text-blue-400 transition cursor-pointer text-left"
title="Просмотреть профиль"
>
{user.username}
</button>
<p className="text-sm text-gray-400">
{user.role === 'admin' ? 'Администратор' : user.role === 'support' ? 'Тех. поддержка' : user.role === 'banned' ? 'Забанен' : 'Пользователь'}
</p>