diff --git a/devfront/src/features/clients/ClientsPage.tsx b/devfront/src/features/clients/ClientsPage.tsx index e12df84e..7be3d883 100644 --- a/devfront/src/features/clients/ClientsPage.tsx +++ b/devfront/src/features/clients/ClientsPage.tsx @@ -52,6 +52,7 @@ import { Textarea } from "../../components/ui/textarea"; import { type ClientSummary, type DevAuditLog, + fetchDevUser, fetchClients, fetchDeveloperRequestStatus, fetchDevStats, @@ -68,6 +69,7 @@ import { formatAuditDateParts, formatAuditValue, parseAuditDetails, + resolveAuditActor, } from "../../../../common/core/audit"; type ClientSortKey = "application" | "id" | "type" | "status" | "createdAt"; @@ -76,6 +78,8 @@ type RecentClientChange = { eventId: string; clientId: string; clientName: string; + actorId: string; + actorName: string; action: string; actionLabel: string; timestamp: string; @@ -488,6 +492,8 @@ function ClientsPage() { eventId: item.event_id, clientId, clientName: clientNameById.get(clientId) || clientId, + actorId: resolveAuditActor(item, details), + actorName: "", action, actionLabel: getRecentClientActionLabel(action), timestamp: item.timestamp, @@ -498,10 +504,45 @@ function ClientsPage() { .slice(0, 4); }, [clients, recentAuditData?.items]); - const recentChangedClientCount = useMemo(() => { - return new Set(recentClientChanges.map((item) => item.clientId)).size; + const recentClientActorIds = useMemo(() => { + return Array.from( + new Set( + recentClientChanges + .map((item) => item.actorId.trim()) + .filter((actorId) => actorId && actorId !== "-"), + ), + ); }, [recentClientChanges]); + const { data: recentClientActors } = useQuery({ + queryKey: ["recent-client-actors", recentClientActorIds], + queryFn: async () => { + const entries = await Promise.all( + recentClientActorIds.map(async (actorId) => { + try { + const user = await fetchDevUser(actorId); + return [actorId, user.name || actorId] as const; + } catch { + return [actorId, actorId] as const; + } + }), + ); + return Object.fromEntries(entries); + }, + enabled: recentClientActorIds.length > 0, + }); + + const recentClientChangesWithActors = useMemo(() => { + return recentClientChanges.map((item) => ({ + ...item, + actorName: recentClientActors?.[item.actorId] || item.actorId, + })); + }, [recentClientActors, recentClientChanges]); + + const recentChangedClientCount = useMemo(() => { + return new Set(recentClientChangesWithActors.map((item) => item.clientId)).size; + }, [recentClientChangesWithActors]); + const isLoading = isLoadingClients || isLoadingStats || @@ -1052,7 +1093,7 @@ function ClientsPage() { )} ) : ( - recentClientChanges.map((item) => { + recentClientChangesWithActors.map((item) => { const { date, time } = formatAuditDateParts(item.timestamp); return (
{item.clientId} + + {item.actorName} + + + {item.actorId} + {item.actionLabel}
diff --git a/devfront/src/lib/devApi.ts b/devfront/src/lib/devApi.ts index 502f8f82..28cfe077 100644 --- a/devfront/src/lib/devApi.ts +++ b/devfront/src/lib/devApi.ts @@ -177,6 +177,27 @@ export type DevAssignableUserListResponse = { items: DevAssignableUser[]; }; +export type DevUserSummary = { + id: string; + email: string; + loginId?: string; + name: string; + phone?: string; + role: string; + status: string; + tenantSlug?: string; + companyCode?: string; + tenant?: TenantSummary; + joinedTenants?: TenantSummary[]; + metadata?: Record; + department?: string; + grade?: string; + position?: string; + jobTitle?: string; + createdAt: string; + updatedAt: string; +}; + export type ConsentSummary = { subject: string; userName?: string; @@ -290,6 +311,11 @@ export async function fetchDevUsers( return data; } +export async function fetchDevUser(userId: string) { + const { data } = await apiClient.get(`/admin/users/${userId}`); + return data; +} + export async function addClientRelation( clientId: string, payload: ClientRelationUpsertRequest,