Files
GMW/services/frontend/src/hooks/use-dashboard.ts
T
asepharyana 30d640ca5b 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
2026-09-02 00:01:12 +07:00

100 lines
2.4 KiB
TypeScript

import useSWR from "swr";
import { dashboardApi } from "@/lib/api";
import type {
DashboardActivity,
DashboardChannelDetail,
DashboardStats,
DashboardUserDetail,
PaginatedUsers,
TopReactedMessage,
TopReactor,
} from "@/lib/types";
/**
* Server-seeded SWR hooks.
*
* SSR pages fetch the initial payload on the server and hand it here as
* `initialData` — the first render is server data, and SWR takes over for
* revalidation from then on (no blank-spinner-first-load).
*/
export function useStats(initialData?: DashboardStats) {
return useSWR<DashboardStats>(
["dashboard-stats"],
() => dashboardApi.getStats(),
{ fallbackData: initialData },
);
}
export function useActivity(days = 14, initialData?: DashboardActivity) {
return useSWR<DashboardActivity>(
["dashboard-activity", days],
() => dashboardApi.getActivity(days),
{ fallbackData: initialData },
);
}
export function useUsers(initialData?: PaginatedUsers) {
return useSWR<PaginatedUsers>(
["dashboard-users"],
() => dashboardApi.listUsers(50),
{ fallbackData: initialData },
);
}
export function useUserSearch(search?: string) {
return useSWR(
["dashboard-users", search ?? ""],
async () => {
const res = await dashboardApi.listUsers(20, undefined, search);
return res.data;
},
{
keepPreviousData: true,
},
);
}
export function useChannels(guildId?: string, search?: string) {
return useSWR(
["dashboard-channels", guildId ?? "__all__", search ?? ""],
async () => {
const res = await dashboardApi.listChannels(
20,
search,
guildId || undefined,
);
return res.data;
},
{
keepPreviousData: true,
},
);
}
export function useUserDetail(userId: string | null) {
return useSWR<DashboardUserDetail>(
userId ? (["dashboard-user", userId] as const) : null,
() => dashboardApi.getUserDetail(userId ?? ""),
);
}
export function useChannelDetail(channelId: string | null) {
return useSWR<DashboardChannelDetail>(
channelId ? (["dashboard-channel", channelId] as const) : null,
() => dashboardApi.getChannelDetail(channelId ?? ""),
);
}
export function useTopReactions() {
return useSWR<TopReactedMessage[]>(["dashboard-reactions"], () =>
dashboardApi.getTopReactions(20),
);
}
export function useTopReactors() {
return useSWR<TopReactor[]>(["dashboard-reactors"], () =>
dashboardApi.getTopReactors(20),
);
}