feat(frontend): partial user lookup

This commit is contained in:
sam 2024-12-28 11:39:22 -05:00
parent 9d3d46bf33
commit db22e35f0d
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
7 changed files with 157 additions and 3 deletions

View file

@ -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[];
};

View file

@ -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>

View file

@ -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">

View 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 };
},
};

View 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>

View file

@ -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 };
};

View 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}