From 20e9d8ba1f05534927831cf97759480f1b4347c5 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Wed, 18 Feb 2026 03:20:10 +0100 Subject: [PATCH] fixed user manage panel and updated support tickets to be deleted --- backend/api/views.py | 5 + frontend/src/pages/AdminUserDetail.tsx | 200 +++++++++++++++---------- frontend/src/pages/PublicProfile.tsx | 2 +- frontend/src/pages/Support.tsx | 20 ++- 4 files changed, 147 insertions(+), 80 deletions(-) diff --git a/backend/api/views.py b/backend/api/views.py index 7177742..7ffc6cc 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -598,6 +598,11 @@ class TicketViewSet(viewsets.ModelViewSet): return Response({'status': 'updated'}) return Response({'error': 'Invalid status'}, status=status.HTTP_400_BAD_REQUEST) + def destroy(self, request, *args, **kwargs): + if not request.user.is_superuser: + raise permissions.PermissionDenied("Only admins can delete tickets.") + return super().destroy(request, *args, **kwargs) + class AdminViewSet(viewsets.ViewSet): permission_classes = [permissions.IsAdminUser] diff --git a/frontend/src/pages/AdminUserDetail.tsx b/frontend/src/pages/AdminUserDetail.tsx index f488fcb..355b4df 100644 --- a/frontend/src/pages/AdminUserDetail.tsx +++ b/frontend/src/pages/AdminUserDetail.tsx @@ -1,12 +1,14 @@ import React, { useState, useEffect } from 'react'; import { Container, Typography, Box, Card, CardContent, Button, - Stack, alpha, CircularProgress, Chip, Paper, List, ListItem, ListItemText, - Avatar, Grid, TextField + Stack, alpha, useTheme, CircularProgress, Chip, Paper, 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 api from '../api'; @@ -30,9 +32,11 @@ interface AdminUser { const AdminUserDetail: React.FC = () => { const { t } = useTranslation(); const navigate = useNavigate(); + const theme = useTheme(); const { id } = useParams<{ id: string }>(); const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); + const [activeTab, setActiveTab] = useState(0); // Edit state for Ratings const [editingRatingId, setEditingRatingId] = useState(null); @@ -49,7 +53,7 @@ const AdminUserDetail: React.FC = () => { setUser(res.data); } catch (err) { console.error(err); - navigate('/admin'); + navigate('/admin-panel'); } finally { setLoading(false); } @@ -105,7 +109,7 @@ const AdminUserDetail: React.FC = () => { if (!window.confirm('PERMANENTLY DELETE THIS USER AND ALL DATA? This cannot be undone.')) return; try { await api.delete(`admin-custom/${id}/user_detail/`); - navigate('/admin'); + navigate('/admin-panel'); } catch (err) { alert('Delete failed'); } }; @@ -117,81 +121,67 @@ const AdminUserDetail: React.FC = () => { + {/* Profile Header */} - - - - {user.username.charAt(0).toUpperCase()} - - - {user.username} - {user.email} - - Joined: {formatDate(user.date_joined)} • Last Login: {formatDate(user.last_login)} - - - - {user.is_superuser && } - - - - + + + + + {user.username.charAt(0).toUpperCase()} + + + + + {user.username} + {user.email} + + Joined: {formatDate(user.date_joined)} • Last Login: {formatDate(user.last_login)} + + + + {user.is_superuser && } + + + + + + + - - - - - - {t('admin.ipHistory')} - - - {user.ips.map((ip, idx) => ( - - - - ))} - {user.ips.length === 0 && No IP history available.} - - - - alpha(theme.palette.error.main, 0.05) }}> - {t('admin.dangerZone')} - - Deleting this user will permanently remove their profile, ratings, replies, and all associated metadata. - - - - - + {/* Content Tabs */} + + setActiveTab(v)} variant="fullWidth"> + } label={`Content (${user.ratings.length})`} sx={{ fontWeight: 'bold' }} /> + } label="Security & IP" sx={{ fontWeight: 'bold' }} /> + } label="Danger Zone" sx={{ fontWeight: 'bold', color: 'error.main' }} /> + + + - - User Content + {/* TAB 0: USER CONTENT (Ratings & Replies) */} + {activeTab === 0 && ( + + User Ratings & Discussion {user.ratings.map(rating => ( - - + + {editingRatingId === rating.id ? ( @@ -204,7 +194,8 @@ const AdminUserDetail: React.FC = () => { /> @@ -217,17 +208,23 @@ const AdminUserDetail: React.FC = () => { {rating.flavor_name} - + - - {rating.comment ? : No comment} - + + {rating.comment ? ( + + + + ) : ( + No comment + )} + {rating.replies.length > 0 && ( - + {rating.replies.map((reply: any) => ( {editingReplyId === reply.id ? ( @@ -241,13 +238,15 @@ const AdminUserDetail: React.FC = () => { ) : ( <> - {reply.user} + + {reply.user} + - + )} @@ -259,10 +258,55 @@ const AdminUserDetail: React.FC = () => { ))} - {user.ratings.length === 0 && No ratings yet.} + {user.ratings.length === 0 && ( + + No ratings yet. + + )} - - + + )} + + {/* TAB 1: SECURITY & IP */} + {activeTab === 1 && ( + + + {t('admin.ipHistory')} + + + {user.ips.map((ip, idx) => ( + + + + ))} + {user.ips.length === 0 && No IP history available.} + + + )} + + {/* TAB 2: DANGER ZONE */} + {activeTab === 2 && ( + alpha(theme.palette.error.main, 0.05) }}> + {t('admin.dangerZone')} + + Deleting this user will permanently remove their profile, all their ratings, replies, and IP history. + This action is irreversible. + + + + )} ); }; diff --git a/frontend/src/pages/PublicProfile.tsx b/frontend/src/pages/PublicProfile.tsx index 4774dbb..e30aa7c 100644 --- a/frontend/src/pages/PublicProfile.tsx +++ b/frontend/src/pages/PublicProfile.tsx @@ -177,7 +177,7 @@ const PublicProfile: React.FC = ({ adminMode }) => {