Files
GMW/services/frontend/src/features/dashboard/hooks/useDashboard.ts
T
MythEclipseandClaude 30f8d7cce3 feat: replace corrections/tuner with dashboard module
Replace the corrections/adaptive-prompt-tuner feature with a new
dashboard module providing server stats and user profile overview.

Backend:
- Add dashboard module (routes, service, repository) with stats + user list + user detail endpoints
- Remove corrections module entirely
- Wire dashboard router in app.ts

Frontend:
- Add dashboard feature (DashboardStats, UserSummaryList, UserProfileDetail components + useDashboard hook)
- Remove tuner feature (CorrectionStats, CorrectionHistory, SubmitCorrection, useCorrections)
- Update API client from corrections → dashboard types/fns
- Rename tab 'tuner' → 'dashboard'
- Update MobileTabBar, Header, Sidebar links

Tests:
- Expand backend placeholder test with dashboard assertions
- Expand discord-gateway placeholder test with config/channel assertions

AI moderation:
- llmModerationClient: improve status/reply detection, expand safety categories, fix timer reset
- userProfileLearner: fix isReply refinement
- userProfileStore: add pending cache check
- messageMetadata: add crosspost type mapping
- migrate.ts: improve partial-index safety in schema push

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-13 11:24:42 +07:00

136 lines
3.4 KiB
TypeScript

import { useCallback, useEffect, useState } from "react";
import {
type DashboardStats,
type DashboardUser,
type DashboardUserDetail,
getDashboardStats,
getDashboardUserDetail,
listDashboardUsers,
} from "../../../shared/api/client";
const logger = console;
/**
* Fetch dashboard aggregate stats.
*/
export function useDashboardStats() {
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetch = useCallback(async () => {
setLoading(true);
setError(null);
try {
const data = await getDashboardStats();
setStats(data);
} catch (e) {
const msg = e instanceof Error ? e.message : "Failed to load stats";
setError(msg);
logger.error("[useDashboardStats]", msg);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetch().catch(() => undefined);
}, [fetch]);
return { stats, loading, error, refetch: fetch };
}
/**
* Fetch paginated user list with optional search.
*/
export function useDashboardUsers() {
const [users, setUsers] = useState<DashboardUser[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [nextCursor, setNextCursor] = useState<string | null>(null);
const [search, setSearch] = useState("");
const fetchUsers = useCallback(
async (cursor?: string) => {
setLoading(true);
setError(null);
try {
const result = await listDashboardUsers({
limit: 20,
cursor,
search: search || undefined,
});
if (cursor) {
setUsers((prev) => [...prev, ...result.data]);
} else {
setUsers(result.data);
}
setNextCursor(result.nextCursor);
} catch (e) {
const msg = e instanceof Error ? e.message : "Failed to load users";
setError(msg);
logger.error("[useDashboardUsers]", msg);
} finally {
setLoading(false);
}
},
[search],
);
useEffect(() => {
fetchUsers().catch(() => undefined);
}, [fetchUsers]);
const loadMore = useCallback(() => {
if (nextCursor && !loading) {
fetchUsers(nextCursor).catch(() => undefined);
}
}, [nextCursor, loading, fetchUsers]);
return {
users,
loading,
error,
search,
setSearch,
loadMore,
hasMore: !!nextCursor,
refetch: () => fetchUsers().catch(() => undefined),
};
}
/**
* Fetch a single user detail by userId.
*/
export function useDashboardUserDetail(userId: string | null) {
const [detail, setDetail] = useState<DashboardUserDetail | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetch = useCallback(async () => {
if (!userId) return;
setLoading(true);
setError(null);
try {
const data = await getDashboardUserDetail(userId);
if (!data) {
setError("User not found");
return;
}
setDetail(data);
} catch (e) {
const msg = e instanceof Error ? e.message : "Failed to load user detail";
setError(msg);
logger.error("[useDashboardUserDetail]", msg);
} finally {
setLoading(false);
}
}, [userId]);
useEffect(() => {
fetch().catch(() => undefined);
}, [fetch]);
return { detail, loading, error, refetch: fetch };
}