feat(frontend): partial user lookup
This commit is contained in:
		
							parent
							
								
									9d3d46bf33
								
							
						
					
					
						commit
						db22e35f0d
					
				
					 7 changed files with 157 additions and 3 deletions
				
			
		|  | @ -1,5 +1,5 @@ | |||
| import type { Member } from "./member"; | ||||
| import type { PartialMember, PartialUser, User } from "./user"; | ||||
| import type { AuthMethod, PartialMember, PartialUser, User } from "./user"; | ||||
| 
 | ||||
| export type CreateReportRequest = { | ||||
| 	reason: ReportReason; | ||||
|  | @ -70,3 +70,13 @@ export type PartialReport = { | |||
| 	context: string | null; | ||||
| 	target_type: "USER" | "MEMBER"; | ||||
| }; | ||||
| 
 | ||||
| export type QueriedUser = { | ||||
| 	user: User; | ||||
| 	member_list_hidden: boolean; | ||||
| 	last_active: string; | ||||
| 	last_sid_reroll: string; | ||||
| 	suspended: boolean; | ||||
| 	deleted: boolean; | ||||
| 	auth_methods?: AuthMethod[]; | ||||
| }; | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ | |||
| 	import { t } from "$lib/i18n"; | ||||
| 	import type { AuthMethod } from "$api/models"; | ||||
| 
 | ||||
| 	type Props = { method: AuthMethod; canRemove: boolean }; | ||||
| 	let { method, canRemove }: Props = $props(); | ||||
| 	type Props = { method: AuthMethod; canRemove: boolean; showType?: boolean }; | ||||
| 	let { method, canRemove, showType }: Props = $props(); | ||||
| 
 | ||||
| 	let name = $derived( | ||||
| 		method.type === "EMAIL" ? method.remote_id : (method.remote_username ?? method.remote_id), | ||||
|  | @ -14,6 +14,9 @@ | |||
| <div class="list-group-item"> | ||||
| 	<div class="row"> | ||||
| 		<div class="col"> | ||||
| 			{#if showType} | ||||
| 				<code>{method.type}</code>: | ||||
| 			{/if} | ||||
| 			{name} | ||||
| 			{#if showId}({method.remote_id}){/if} | ||||
| 		</div> | ||||
|  |  | |||
|  | @ -41,6 +41,13 @@ | |||
| 				> | ||||
| 					Audit log | ||||
| 				</a> | ||||
| 				<a | ||||
| 					href="/admin/lookup" | ||||
| 					class="list-group-item list-group-item-action" | ||||
| 					class:active={isActive("/admin/lookup", true)} | ||||
| 				> | ||||
| 					Lookup | ||||
| 				</a> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="col-md-9"> | ||||
|  |  | |||
							
								
								
									
										27
									
								
								Foxnouns.Frontend/src/routes/admin/lookup/+page.server.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Foxnouns.Frontend/src/routes/admin/lookup/+page.server.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| import { apiRequest } from "$api"; | ||||
| import { redirect } from "@sveltejs/kit"; | ||||
| 
 | ||||
| export const actions = { | ||||
| 	default: async ({ request, fetch, cookies }) => { | ||||
| 		const body = await request.formData(); | ||||
| 		const query = body.get("query") as string; | ||||
| 		const fuzzy = body.get("fuzzy") === "yes"; | ||||
| 
 | ||||
| 		const users = await apiRequest<Array<{ id: string; username: string }>>( | ||||
| 			"POST", | ||||
| 			"/moderation/lookup", | ||||
| 			{ | ||||
| 				fetch, | ||||
| 				cookies, | ||||
| 				body: { | ||||
| 					query, | ||||
| 					fuzzy, | ||||
| 				}, | ||||
| 			}, | ||||
| 		); | ||||
| 
 | ||||
| 		if (!fuzzy && users.length > 0) redirect(303, `/admin/lookup/${users[0].id}`); | ||||
| 
 | ||||
| 		return { users }; | ||||
| 	}, | ||||
| }; | ||||
							
								
								
									
										33
									
								
								Foxnouns.Frontend/src/routes/admin/lookup/+page.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Foxnouns.Frontend/src/routes/admin/lookup/+page.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| <script lang="ts"> | ||||
| 	import type { ActionData } from "./$types"; | ||||
| 
 | ||||
| 	type Props = { form: ActionData }; | ||||
| 	let { form }: Props = $props(); | ||||
| </script> | ||||
| 
 | ||||
| <svelte:head> | ||||
| 	<title>Look up a user • pronouns.cc</title> | ||||
| </svelte:head> | ||||
| 
 | ||||
| <h1>Look up a user</h1> | ||||
| 
 | ||||
| <form method="POST"> | ||||
| 	<div class="input-group w-lg-50 mb-2"> | ||||
| 		<input type="text" class="form-control" name="query" placeholder="Query" required /> | ||||
| 		<button class="btn btn-primary" type="submit">Search</button> | ||||
| 	</div> | ||||
| 	<div class="form-check mb-3"> | ||||
| 		<input class="form-check-input" type="checkbox" value="yes" name="fuzzy" id="fuzzy" /> | ||||
| 		<label class="form-check-label" for="fuzzy">Fuzzy?</label> | ||||
| 	</div> | ||||
| </form> | ||||
| 
 | ||||
| <div class="list-group"> | ||||
| 	{#each form?.users || [] as user (user.id)} | ||||
| 		<a href="/admin/lookup/{user.id}" class="list-group-item list-group-item-action"> | ||||
| 			{user.username} <span class="text-secondary">({user.id})</span> | ||||
| 		</a> | ||||
| 	{:else} | ||||
| 		<div class="list-group-item">No results</div> | ||||
| 	{/each} | ||||
| </div> | ||||
|  | @ -0,0 +1,11 @@ | |||
| import { apiRequest } from "$api"; | ||||
| import type { QueriedUser } from "$api/models/moderation"; | ||||
| 
 | ||||
| export const load = async ({ params, fetch, cookies }) => { | ||||
| 	const user = await apiRequest<QueriedUser>("GET", `/moderation/lookup/${params.id}`, { | ||||
| 		fetch, | ||||
| 		cookies, | ||||
| 	}); | ||||
| 
 | ||||
| 	return { user }; | ||||
| }; | ||||
							
								
								
									
										63
									
								
								Foxnouns.Frontend/src/routes/admin/lookup/[id]/+page.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Foxnouns.Frontend/src/routes/admin/lookup/[id]/+page.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| <script lang="ts"> | ||||
| 	import { idTimestamp } from "$lib"; | ||||
| 	import { DateTime } from "luxon"; | ||||
| 	import type { PageData } from "./$types"; | ||||
| 	import ProfileHeader from "$components/profile/ProfileHeader.svelte"; | ||||
| 	import ProfileFields from "$components/profile/ProfileFields.svelte"; | ||||
| 	import { mergePreferences } from "$api/models"; | ||||
| 	import AuthMethodRow from "$components/settings/AuthMethodRow.svelte"; | ||||
| 
 | ||||
| 	type Props = { data: PageData }; | ||||
| 	let { data }: Props = $props(); | ||||
| 
 | ||||
| 	let createdAt = $derived(idTimestamp(data.user.user.id)); | ||||
| 	let lastActive = $derived(DateTime.fromISO(data.user.last_active)); | ||||
| 	let lastSidReroll = $derived(DateTime.fromISO(data.user.last_sid_reroll)); | ||||
| 
 | ||||
| 	let authMethods = $derived.by(() => { | ||||
| 		if (!data.user.auth_methods) return undefined; | ||||
| 
 | ||||
| 		return data.user.auth_methods.sort((a, b) => a.type.localeCompare(b.type)); | ||||
| 	}); | ||||
| </script> | ||||
| 
 | ||||
| <svelte:head> | ||||
| 	<title>Looking up @{data.user.user.username} • pronouns.cc</title> | ||||
| </svelte:head> | ||||
| 
 | ||||
| <h2>Basic profile</h2> | ||||
| 
 | ||||
| <ProfileHeader name="@{data.user.user.username}" profile={data.user.user} /> | ||||
| 
 | ||||
| <ProfileFields | ||||
| 	profile={data.user.user} | ||||
| 	allPreferences={mergePreferences(data.user.user.custom_preferences)} | ||||
| /> | ||||
| 
 | ||||
| <h2>Extra information</h2> | ||||
| 
 | ||||
| <table class="table table-striped table-hover table-bordered"> | ||||
| 	<tbody> | ||||
| 		<tr> | ||||
| 			<th scope="row">Created at</th> | ||||
| 			<td>{createdAt.toLocaleString(DateTime.DATETIME_MED)}</td> | ||||
| 		</tr> | ||||
| 		<tr> | ||||
| 			<th scope="row">Last active</th> | ||||
| 			<td>{lastActive.toLocaleString(DateTime.DATETIME_MED)}</td> | ||||
| 		</tr> | ||||
| 		<tr> | ||||
| 			<th scope="row">Last SID reroll</th> | ||||
| 			<td>{lastSidReroll.toLocaleString(DateTime.DATETIME_MED)}</td> | ||||
| 		</tr> | ||||
| 	</tbody> | ||||
| </table> | ||||
| 
 | ||||
| {#if authMethods} | ||||
| 	<h2>Authentication methods</h2> | ||||
| 	<div class="list-group"> | ||||
| 		{#each authMethods as method (method.id)} | ||||
| 			<AuthMethodRow {method} canRemove={false} showType /> | ||||
| 		{/each} | ||||
| 	</div> | ||||
| {/if} | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue