feat(fe): add Member Intelligence (/users) page wired to full reputation data

- New /users route: member roster (trust score, clean/flagged counts, message volume)
  + inspector detail (trust breakdown, clean streak, infractions, AI profile,
  recent messages) with live search
- Backend dashboard.listUsers/getUserDetail now JOIN user_reputations to expose
  trust_score, clean_message_streak, total_infractions, last_infraction_at +
  warn_count/clean_count breakdowns
- Frontend types updated to match; useUsers refactored to PaginatedUsers shape,
  added useUserSearch for the old search behavior
- Nav rail + mobile nav: add Users entry
This commit is contained in:
asepharyana committed 2026-09-02 00:01:12 +07:00
1 parent 7c21629404
commit 30d640ca5b
8 files changed
+456 -3

No files matched your search

+5
View File
@@ -71,6 +71,11 @@ export async function getActivity(days = 14): Promise<DashboardActivity> {
days,
}) as unknown as Promise<DashboardActivity>;
}
export async function getUsers(limit = 20) {
return serverOrpc().dashboard.users({
limit,
}) as unknown as Promise<import("@/lib/types").PaginatedUsers>;
}
// ---- Media ----
export async function getMediaStatus(): Promise<MediaState> {
+7
View File
@@ -7,6 +7,7 @@ import {
Music,
Search,
Shield,
Users,
} from "lucide-react";
export interface NavItem {
@@ -33,6 +34,12 @@ export const navItems: NavItem[] = [
icon: MessageSquare,
matchPrefix: "/messages",
},
{
href: "/users",
label: "Users",
icon: Users,
matchPrefix: "/users",
},
{
href: "/voice",
label: "Voice",
@@ -54,9 +54,13 @@ export interface DashboardUser {
profile_summary?: string | null;
total_messages: number;
flagged_count: number;
clean_count?: number;
warn_count?: number;
last_message_at?: number | null;
trust_score?: number | null;
clean_message_streak?: number | null;
total_infractions?: number | null;
last_infraction_at?: number | null;
}
export interface DashboardUserDetail extends DashboardUser {
@@ -64,6 +68,7 @@ export interface DashboardUserDetail extends DashboardUser {
clean_message_streak: number | null;
total_infractions: number | null;
clean_count: number;
warn_count?: number;
recent_messages: MessageRecord[];
}