import React, { useEffect, useState } from 'react'; import { Typography, Box, CardContent, Button, Stack, CircularProgress, Chip, List, ListItem, ListItemText, Avatar, Grid, TextField, Tabs, Tab, Divider, } from '@mui/material'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import DeleteIcon from '@mui/icons-material/Delete'; import SecurityIcon from '@mui/icons-material/Security'; import HistoryIcon from '@mui/icons-material/History'; import RateReviewIcon from '@mui/icons-material/RateReview'; import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router-dom'; import { useAdminUserDetail } from '../api/queries/useAdminQueries'; import { useDeleteUser, useToggleUserActive } from '../api/mutations/useAdminMutations'; import { useDeleteRating, useDeleteReply, useUpdateRating, } from '../api/mutations/useRatingMutations'; import { useUpdateReply } from '../api/mutations/useReplyMutations'; import { useTitle } from '../hooks/useTitle'; import { formatDate } from '../utils/date'; import MentionTextField from '../components/MentionTextField'; import RichText from '../components/RichText'; import { PageShell, SectionHeader, GlassCard, GlassPaper, FormCard, EmptyState, } from '../components/ui'; import { useToast } from '../hooks/useToast'; import { useConfirm } from '../hooks/useConfirm'; interface AdminReply { id: number; user: string; text: string; created_at: string; } interface AdminRating { id: number; flavor_name: string; score: number; comment: string; created_at: string; replies: AdminReply[]; } interface AdminUser { id: number; username: string; email: string; is_active: boolean; is_superuser: boolean; date_joined: string; last_login: string; ips: string[]; ratings: AdminRating[]; } const AdminUserDetail: React.FC = () => { const { t } = useTranslation(); const navigate = useNavigate(); const { id } = useParams<{ id: string }>(); const { data: user, isLoading: loading, error, } = useAdminUserDetail(id) as { data: AdminUser | undefined; isLoading: boolean; error: unknown; }; const toggleActive = useToggleUserActive(); const deleteUserMutation = useDeleteUser(); const updateRatingMutation = useUpdateRating(); const deleteRatingMutation = useDeleteRating(); const updateReplyMutation = useUpdateReply(); const deleteReplyMutation = useDeleteReply(); const [activeTab, setActiveTab] = useState(0); const [editingRatingId, setEditingRatingId] = useState(null); const [editScore, setEditScore] = useState(0); const [editComment, setEditComment] = useState(''); const [editingReplyId, setEditingReplyId] = useState(null); const [editReplyText, setEditReplyText] = useState(''); const { notify } = useToast(); const { confirm } = useConfirm(); useEffect(() => { if (error) navigate('/admin-panel'); }, [error, navigate]); useTitle(user ? `${t('admin.manageUser')}: ${user.username}` : t('admin.userMgmtTitle')); const handleToggleActive = async () => { if (!user || !id) return; try { await toggleActive.mutateAsync({ id, isActive: user.is_active }); } catch { notify({ message: t('admin.actionFailed'), severity: 'error' }); } }; const handleUpdateRating = async (ratingId: number) => { try { await updateRatingMutation.mutateAsync({ id: ratingId, score: editScore, comment: editComment, }); setEditingRatingId(null); } catch { notify({ message: t('admin.updateFailed'), severity: 'error' }); } }; const handleDeleteRating = async (ratingId: number) => { if (!(await confirm({ message: t('admin.deleteRatingConfirm'), danger: true }))) return; try { await deleteRatingMutation.mutateAsync(ratingId); } catch { notify({ message: t('admin.deleteFailed'), severity: 'error' }); } }; const handleUpdateReply = async (replyId: number) => { try { await updateReplyMutation.mutateAsync({ replyId, text: editReplyText }); setEditingReplyId(null); } catch { notify({ message: t('admin.updateFailed'), severity: 'error' }); } }; const handleDeleteReply = async (replyId: number) => { if (!(await confirm({ message: t('admin.deleteReplyConfirm'), danger: true }))) return; try { await deleteReplyMutation.mutateAsync(replyId); } catch { notify({ message: t('admin.deleteFailed'), severity: 'error' }); } }; const handleDelete = async () => { if (!id) return; const ok = await confirm({ title: t('admin.deleteUserTitle'), message: t('admin.deleteUserConfirm'), confirmLabel: t('admin.deleteForever'), danger: true, }); if (!ok) return; try { await deleteUserMutation.mutateAsync(id); navigate('/admin-panel'); } catch { notify({ message: t('admin.deleteFailed'), severity: 'error' }); } }; if (loading) return ( ); if (!user) return {t('admin.userNotFound')}; return ( {user.username.charAt(0).toUpperCase()} {user.username} {user.email} {t('admin.joinedLabel')}: {formatDate(user.date_joined)} •{' '} {t('admin.lastLoginLabel')}: {formatDate(user.last_login)} {user.is_superuser && ( )} setActiveTab(v)} variant="fullWidth"> } label={`${t('admin.tabContent')} (${user.ratings.length})`} sx={{ fontWeight: 700 }} /> } label={t('admin.tabSecurity')} sx={{ fontWeight: 700 }} /> } label={t('admin.tabDanger')} sx={{ fontWeight: 700, color: 'error.main' }} /> {activeTab === 0 && ( {user.ratings.map((rating) => ( {editingRatingId === rating.id ? ( {t('admin.scoreLabel')}: setEditScore(Number(e.target.value))} sx={{ width: 80 }} /> ) : ( <> {rating.flavor_name} {rating.comment ? ( ) : ( {t('admin.noCommentShort')} )} {rating.replies.length > 0 && ( {rating.replies.map((reply) => ( {editingReplyId === reply.id ? ( ) : ( <> {reply.user} )} ))} )} )} ))} {user.ratings.length === 0 && } )} {activeTab === 1 && ( {t('admin.ipHistory')} {user.ips.map((ip, idx) => ( ))} {user.ips.length === 0 && ( {t('admin.noIpHistory')} )} )} {activeTab === 2 && ( } onClick={handleDelete} sx={{ borderRadius: 2, fontWeight: 700, px: 4 }} > {t('admin.deleteUser')} } > {t('admin.deleteUserNote')} )} ); }; export default AdminUserDetail;