From 43003957eb016f4405d592e5aa9366f3a3ed237e Mon Sep 17 00:00:00 2001 From: Anais Saraiva Date: Mon, 21 Sep 2026 21:57:36 +0200 Subject: [PATCH] :3 --- frontend/src/components/AdminCenter.tsx | 2 +- frontend/src/components/Chat.tsx | 9 ---- frontend/src/components/ReportsPanel.tsx | 43 +++++++++++++++++++ frontend/src/lib/i18n.ts | 54 ++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/AdminCenter.tsx b/frontend/src/components/AdminCenter.tsx index 2e0c56b..593b0f9 100644 --- a/frontend/src/components/AdminCenter.tsx +++ b/frontend/src/components/AdminCenter.tsx @@ -67,7 +67,7 @@ export function AdminCenter() { try { const res = await api.adminReportList(); setOpenReportCount( - res.reports.filter((r) => r.status === "open" || r.status === "reviewing").length, + res.reports.filter((r) => (r.status === "open" || r.status === "reviewing") && r.reporter_shadowbanned !== 1).length, ); } catch { // Transient failure: keep last count. diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index 4afd63c..4069a30 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -126,15 +126,6 @@ function ReportDialog({ open, onClose, conversationId, messageIndex, content, pr - ) : ( -
-

- {t("report.sent")} -

-
- -
-
) : (
diff --git a/frontend/src/components/ReportsPanel.tsx b/frontend/src/components/ReportsPanel.tsx index 929e8b8..2fe1df9 100644 --- a/frontend/src/components/ReportsPanel.tsx +++ b/frontend/src/components/ReportsPanel.tsx @@ -136,8 +136,10 @@ function ReportDetail({ report, onChanged }: { report: Report; onChanged: () => const [note, setNote] = useState(report.admin_note); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); + const [confirmBan, setConfirmBan] = useState(null); const { t } = useT(); const closed = report.status === "resolved" || report.status === "dismissed"; + const isBanned = report.reporter_shadowbanned === 1; useEffect(() => { setNote(report.admin_note); @@ -156,8 +158,29 @@ function ReportDetail({ report, onChanged }: { report: Report; onChanged: () => } } + async function toggleBan(shadowbanned: boolean) { + setBusy(true); + setError(""); + try { + await api.adminShadowban(report.reporter_id, shadowbanned); + onChanged(); + } catch (err) { + setError(err instanceof Error ? err.message : t("reports.updateFailed")); + } finally { + setBusy(false); + } + } + return (
+ {report.prompt && ( +
+

{t("reports.prompt")}

+
+ {report.prompt} +
+
+ )}

{t("reports.response")}

@@ -176,6 +199,26 @@ function ReportDetail({ report, onChanged }: { report: Report; onChanged: () => )}
+
+ + {isBanned && {t("reports.unreliable")}} +
+ setConfirmBan(null)} + onConfirm={() => { + const v = confirmBan; + setConfirmBan(null); + if (v !== null) void toggleBan(v); + }} + title={t(confirmBan === false ? "reports.unshadowbanTitle" : "reports.shadowbanTitle")} + message={t(confirmBan === false ? "reports.unshadowbanMsg" : "reports.shadowbanMsg", { user: report.reporter_name })} + confirmLabel={t(confirmBan === false ? "reports.unshadowban" : "reports.shadowban")} + /> + {!closed ? (
diff --git a/frontend/src/lib/i18n.ts b/frontend/src/lib/i18n.ts index 38dea3b..1f7537e 100644 --- a/frontend/src/lib/i18n.ts +++ b/frontend/src/lib/i18n.ts @@ -467,6 +467,15 @@ const en = { "reports.btnDismiss": "Dismiss", "reports.updateFailed": "Update failed", "reports.closedNote": "Report {status}.", + "report.alreadyReported": "This response was already reported.", + "reports.prompt": "User prompt", + "reports.unreliable": "Unreliable reporter", + "reports.shadowban": "Shadow-ban reporter", + "reports.unshadowban": "Lift shadow-ban", + "reports.shadowbanTitle": "Shadow-ban this reporter?", + "reports.shadowbanMsg": "@{user}'s reports will still be accepted but marked unreliable and hidden from the badge.", + "reports.unshadowbanTitle": "Lift the shadow-ban?", + "reports.unshadowbanMsg": "@{user}'s reports will count toward the badge again.", "review.title": "Access request", "review.loadingTicket": "Loading ticket…", @@ -961,6 +970,15 @@ const es: Dict = { "reports.btnDismiss": "Descartar", "reports.updateFailed": "No se pudo actualizar", "reports.closedNote": "Denuncia {status}.", + "report.alreadyReported": "Esta respuesta ya fue denunciada.", + "reports.prompt": "Pregunta del usuario", + "reports.unreliable": "Denunciante no fiable", + "reports.shadowban": "Silenciar denunciante", + "reports.unshadowban": "Quitar silencio", + "reports.shadowbanTitle": "¿Silenciar a este denunciante?", + "reports.shadowbanMsg": "Las denuncias de @{user} se seguirán aceptando, pero se marcarán como no fiables y no contarán en el contador.", + "reports.unshadowbanTitle": "¿Quitar el silencio?", + "reports.unshadowbanMsg": "Las denuncias de @{user} volverán a contar.", "review.title": "Solicitud de acceso", "review.loadingTicket": "Cargando ticket…", @@ -1452,6 +1470,15 @@ const fr: Dict = { "reports.btnDismiss": "Rejeter", "reports.updateFailed": "Mise à jour impossible", "reports.closedNote": "Signalement {status}.", + "report.alreadyReported": "Cette réponse a déjà été signalée.", + "reports.prompt": "Demande de l'utilisateur", + "reports.unreliable": "Signaleur non fiable", + "reports.shadowban": "Shadow-ban le signaleur", + "reports.unshadowban": "Lever le shadow-ban", + "reports.shadowbanTitle": "Shadow-ban ce signaleur ?", + "reports.shadowbanMsg": "Les signalements de @{user} seront toujours acceptés mais marqués non fiables et exclus du compteur.", + "reports.unshadowbanTitle": "Lever le shadow-ban ?", + "reports.unshadowbanMsg": "Les signalements de @{user} compteront à nouveau.", "review.title": "Demande d'accès", "review.loadingTicket": "Chargement du ticket…", @@ -1931,6 +1958,15 @@ const ru: Dict = { "reports.btnDismiss": "Отклонить", "reports.updateFailed": "Не обновилось", "reports.closedNote": "Жалоба {status}.", + "report.alreadyReported": "На этот ответ уже пожаловались.", + "reports.prompt": "Запрос пользователя", + "reports.unreliable": "Ненадёжный автор", + "reports.shadowban": "Теневой бан", + "reports.unshadowban": "Снять теневой бан", + "reports.shadowbanTitle": "Теневой бан для этого автора?", + "reports.shadowbanMsg": "Жалобы @{user} будут приниматься, но помечаться как ненадёжные и не учитываться в счётчике.", + "reports.unshadowbanTitle": "Снять теневой бан?", + "reports.unshadowbanMsg": "Жалобы @{user} снова будут учитываться.", "review.title": "Заявка на доступ", "review.loadingTicket": "Загружаем тикет…", @@ -2415,6 +2451,15 @@ const it: Dict = { "reports.btnDismiss": "Respingi", "reports.updateFailed": "Aggiornamento fallito", "reports.closedNote": "Segnalazione {status}.", + "report.alreadyReported": "Questa risposta è già stata segnalata.", + "reports.prompt": "Richiesta dell'utente", + "reports.unreliable": "Autore inaffidabile", + "reports.shadowban": "Silenzia autore", + "reports.unshadowban": "Togli silenzio", + "reports.shadowbanTitle": "Silenziare questo autore?", + "reports.shadowbanMsg": "Le segnalazioni di @{user} saranno comunque accettate ma marcate come inaffidabili ed escluse dal conteggio.", + "reports.unshadowbanTitle": "Togliere il silenzio?", + "reports.unshadowbanMsg": "Le segnalazioni di @{user} conteranno di nuovo.", "review.title": "Richiesta di accesso", "review.loadingTicket": "Carico ticket…", @@ -2890,6 +2935,15 @@ const pt: Dict = { "reports.btnDismiss": "Rejeitar", "reports.updateFailed": "Falha ao atualizar", "reports.closedNote": "Denúncia {status}.", + "report.alreadyReported": "Esta resposta já foi denunciada.", + "reports.prompt": "Pergunta do utilizador", + "reports.unreliable": "Autor pouco fiável", + "reports.shadowban": "Silenciar autor", + "reports.unshadowban": "Tirar silêncio", + "reports.shadowbanTitle": "Silenciar este autor?", + "reports.shadowbanMsg": "As denúncias de @{user} continuarão a ser aceites, mas marcadas como pouco fiáveis e excluídas da contagem.", + "reports.unshadowbanTitle": "Tirar o silêncio?", + "reports.unshadowbanMsg": "As denúncias de @{user} voltarão a contar.", "review.title": "Pedido de acesso", "review.loadingTicket": "A carregar ticket…",