feat(frontend): show audit log entry for closed reports
This commit is contained in:
		
							parent
							
								
									cacd3a30b7
								
							
						
					
					
						commit
						6bb01f0bf1
					
				
					 6 changed files with 81 additions and 12 deletions
				
			
		|  | @ -229,6 +229,7 @@ public class ReportsController( | ||||||
|             .Reports.Include(r => r.Reporter) |             .Reports.Include(r => r.Reporter) | ||||||
|             .Include(r => r.TargetUser) |             .Include(r => r.TargetUser) | ||||||
|             .Include(r => r.TargetMember) |             .Include(r => r.TargetMember) | ||||||
|  |             .Include(r => r.AuditLogEntry) | ||||||
|             .FirstOrDefaultAsync(r => r.Id == id, ct); |             .FirstOrDefaultAsync(r => r.Id == id, ct); | ||||||
|         if (report == null) |         if (report == null) | ||||||
|             throw new ApiError.NotFound("No report with that ID found."); |             throw new ApiError.NotFound("No report with that ID found."); | ||||||
|  | @ -243,6 +244,9 @@ public class ReportsController( | ||||||
|                 ), |                 ), | ||||||
|                 Member: report.TargetMember != null |                 Member: report.TargetMember != null | ||||||
|                     ? memberRenderer.RenderMember(report.TargetMember) |                     ? memberRenderer.RenderMember(report.TargetMember) | ||||||
|  |                     : null, | ||||||
|  |                 AuditLogEntry: report.AuditLogEntry != null | ||||||
|  |                     ? moderationRenderer.RenderAuditLogEntry(report.AuditLogEntry) | ||||||
|                     : null |                     : null | ||||||
|             ) |             ) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|  | @ -39,7 +39,9 @@ public record ReportResponse( | ||||||
| public record ReportDetailResponse( | public record ReportDetailResponse( | ||||||
|     ReportResponse Report, |     ReportResponse Report, | ||||||
|     UserResponse User, |     UserResponse User, | ||||||
|     [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] MemberResponse? Member |     [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] MemberResponse? Member, | ||||||
|  |     [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] | ||||||
|  |         AuditLogResponse? AuditLogEntry | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| public record AuditLogResponse( | public record AuditLogResponse( | ||||||
|  |  | ||||||
|  | @ -75,6 +75,7 @@ export type ReportDetails = { | ||||||
| 	report: Report; | 	report: Report; | ||||||
| 	user: User; | 	user: User; | ||||||
| 	member?: Member; | 	member?: Member; | ||||||
|  | 	audit_log_entry?: AuditLogEntry; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type QueriedUser = { | export type QueriedUser = { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | <script lang="ts"> | ||||||
|  | 	import { AuditLogEntryType, type AuditLogEntry } from "$api/models/moderation"; | ||||||
|  | 	import { idTimestamp } from "$lib"; | ||||||
|  | 	import { renderMarkdown } from "$lib/markdown"; | ||||||
|  | 	import { DateTime } from "luxon"; | ||||||
|  | 	import AuditLogEntity from "./AuditLogEntity.svelte"; | ||||||
|  | 
 | ||||||
|  | 	type Props = { entry: AuditLogEntry }; | ||||||
|  | 	let { entry }: Props = $props(); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <div class="row"> | ||||||
|  | 	<h3 id="report-status"> | ||||||
|  | 		Closed by <AuditLogEntity entity={entry.moderator} /> at | ||||||
|  | 		{idTimestamp(entry.id).toLocaleString(DateTime.DATETIME_MED)} | ||||||
|  | 	</h3> | ||||||
|  | 	<p> | ||||||
|  | 		{#if entry.type === AuditLogEntryType.IgnoreReport} | ||||||
|  | 			Report was ignored | ||||||
|  | 		{:else if entry.type === AuditLogEntryType.WarnUser || entry.type === AuditLogEntryType.WarnUserAndClearProfile} | ||||||
|  | 			User was warned | ||||||
|  | 			{#if entry.cleared_fields && entry.cleared_fields.length > 0} | ||||||
|  | 				<br />Cleared fields: {entry.cleared_fields.join(", ")} | ||||||
|  | 			{/if} | ||||||
|  | 		{:else if entry.type === AuditLogEntryType.SuspendUser} | ||||||
|  | 			User was suspended | ||||||
|  | 		{/if} | ||||||
|  | 	</p> | ||||||
|  | 	<h4>Reason</h4> | ||||||
|  | 	<p> | ||||||
|  | 		{#if entry.reason} | ||||||
|  | 			{@html renderMarkdown(entry.reason)} | ||||||
|  | 		{:else} | ||||||
|  | 			<em class="text-secondary">(no reason given)</em> | ||||||
|  | 		{/if} | ||||||
|  | 	</p> | ||||||
|  | </div> | ||||||
|  | @ -7,7 +7,12 @@ export const load = async ({ params, fetch, cookies }) => { | ||||||
| 		fetch, | 		fetch, | ||||||
| 		cookies, | 		cookies, | ||||||
| 	}); | 	}); | ||||||
| 	return { report: resp.report, user: resp.user, member: resp.member }; | 	return { | ||||||
|  | 		report: resp.report, | ||||||
|  | 		user: resp.user, | ||||||
|  | 		member: resp.member, | ||||||
|  | 		auditLogEntry: resp.audit_log_entry, | ||||||
|  | 	}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const actions = createModactions(); | export const actions = createModactions(); | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| 	import type { Member } from "$api/models/member"; | 	import type { Member } from "$api/models/member"; | ||||||
| 	import type { User } from "$api/models/user"; | 	import type { User } from "$api/models/user"; | ||||||
| 	import ActionForm from "$components/admin/ActionForm.svelte"; | 	import ActionForm from "$components/admin/ActionForm.svelte"; | ||||||
|  | 	import ClosedReportAuditLog from "$components/admin/ClosedReportAuditLog.svelte"; | ||||||
| 	import PartialProfileCard from "$components/admin/PartialProfileCard.svelte"; | 	import PartialProfileCard from "$components/admin/PartialProfileCard.svelte"; | ||||||
| 	import ProfileHeader from "$components/profile/ProfileHeader.svelte"; | 	import ProfileHeader from "$components/profile/ProfileHeader.svelte"; | ||||||
| 	import MemberCard from "$components/profile/user/MemberCard.svelte"; | 	import MemberCard from "$components/profile/user/MemberCard.svelte"; | ||||||
|  | @ -10,13 +11,21 @@ | ||||||
| 
 | 
 | ||||||
| 	type Props = { data: PageData; form: ActionData }; | 	type Props = { data: PageData; form: ActionData }; | ||||||
| 	let { data, form }: Props = $props(); | 	let { data, form }: Props = $props(); | ||||||
| 	let { report, user, member } = $derived(data); | 	let { report, user, auditLogEntry } = $derived(data); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <svelte:head> | <svelte:head> | ||||||
| 	<title>Report on @{user.username} • pronouns.cc</title> | 	<title>Report on @{user.username} • pronouns.cc</title> | ||||||
| </svelte:head> | </svelte:head> | ||||||
| 
 | 
 | ||||||
|  | {#if report.status === "CLOSED"} | ||||||
|  | 	<div class="alert alert-secondary"> | ||||||
|  | 		This report has already been handled. <a href="#report-status" class="alert-link" | ||||||
|  | 			>See audit log entry</a | ||||||
|  | 		> | ||||||
|  | 	</div> | ||||||
|  | {/if} | ||||||
|  | 
 | ||||||
| <div class="row"> | <div class="row"> | ||||||
| 	<div class="col-md"> | 	<div class="col-md"> | ||||||
| 		<h3>Target user</h3> | 		<h3>Target user</h3> | ||||||
|  | @ -55,7 +64,8 @@ | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| <div class="row"> | {#if report.status === "OPEN"} | ||||||
|  | 	<div class="row"> | ||||||
| 		<h3>Take action</h3> | 		<h3>Take action</h3> | ||||||
| 		<ActionForm | 		<ActionForm | ||||||
| 			userId={report.target_user.id} | 			userId={report.target_user.id} | ||||||
|  | @ -63,7 +73,17 @@ | ||||||
| 			memberId={report.target_member?.id} | 			memberId={report.target_member?.id} | ||||||
| 			{form} | 			{form} | ||||||
| 		/> | 		/> | ||||||
| </div> | 	</div> | ||||||
|  | {:else if report.status === "CLOSED" && auditLogEntry} | ||||||
|  | 	<ClosedReportAuditLog entry={auditLogEntry} /> | ||||||
|  | {:else} | ||||||
|  | 	<div class="row"> | ||||||
|  | 		<h3>Closed by an unknown moderator</h3> | ||||||
|  | 		<p> | ||||||
|  | 			<em>This should not happen!</em> | ||||||
|  | 		</p> | ||||||
|  | 	</div> | ||||||
|  | {/if} | ||||||
| 
 | 
 | ||||||
| {#if report.snapshot} | {#if report.snapshot} | ||||||
| 	<h3>Profile at time of report</h3> | 	<h3>Profile at time of report</h3> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue