forked from baron/baron-sso
변경 RP 카드 변경자 표시 추가
This commit is contained in:
@@ -52,6 +52,7 @@ import { Textarea } from "../../components/ui/textarea";
|
|||||||
import {
|
import {
|
||||||
type ClientSummary,
|
type ClientSummary,
|
||||||
type DevAuditLog,
|
type DevAuditLog,
|
||||||
|
fetchDevUser,
|
||||||
fetchClients,
|
fetchClients,
|
||||||
fetchDeveloperRequestStatus,
|
fetchDeveloperRequestStatus,
|
||||||
fetchDevStats,
|
fetchDevStats,
|
||||||
@@ -68,6 +69,7 @@ import {
|
|||||||
formatAuditDateParts,
|
formatAuditDateParts,
|
||||||
formatAuditValue,
|
formatAuditValue,
|
||||||
parseAuditDetails,
|
parseAuditDetails,
|
||||||
|
resolveAuditActor,
|
||||||
} from "../../../../common/core/audit";
|
} from "../../../../common/core/audit";
|
||||||
|
|
||||||
type ClientSortKey = "application" | "id" | "type" | "status" | "createdAt";
|
type ClientSortKey = "application" | "id" | "type" | "status" | "createdAt";
|
||||||
@@ -76,6 +78,8 @@ type RecentClientChange = {
|
|||||||
eventId: string;
|
eventId: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
|
actorId: string;
|
||||||
|
actorName: string;
|
||||||
action: string;
|
action: string;
|
||||||
actionLabel: string;
|
actionLabel: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -488,6 +492,8 @@ function ClientsPage() {
|
|||||||
eventId: item.event_id,
|
eventId: item.event_id,
|
||||||
clientId,
|
clientId,
|
||||||
clientName: clientNameById.get(clientId) || clientId,
|
clientName: clientNameById.get(clientId) || clientId,
|
||||||
|
actorId: resolveAuditActor(item, details),
|
||||||
|
actorName: "",
|
||||||
action,
|
action,
|
||||||
actionLabel: getRecentClientActionLabel(action),
|
actionLabel: getRecentClientActionLabel(action),
|
||||||
timestamp: item.timestamp,
|
timestamp: item.timestamp,
|
||||||
@@ -498,10 +504,45 @@ function ClientsPage() {
|
|||||||
.slice(0, 4);
|
.slice(0, 4);
|
||||||
}, [clients, recentAuditData?.items]);
|
}, [clients, recentAuditData?.items]);
|
||||||
|
|
||||||
const recentChangedClientCount = useMemo(() => {
|
const recentClientActorIds = useMemo(() => {
|
||||||
return new Set(recentClientChanges.map((item) => item.clientId)).size;
|
return Array.from(
|
||||||
|
new Set(
|
||||||
|
recentClientChanges
|
||||||
|
.map((item) => item.actorId.trim())
|
||||||
|
.filter((actorId) => actorId && actorId !== "-"),
|
||||||
|
),
|
||||||
|
);
|
||||||
}, [recentClientChanges]);
|
}, [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 =
|
const isLoading =
|
||||||
isLoadingClients ||
|
isLoadingClients ||
|
||||||
isLoadingStats ||
|
isLoadingStats ||
|
||||||
@@ -1052,7 +1093,7 @@ function ClientsPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
recentClientChanges.map((item) => {
|
recentClientChangesWithActors.map((item) => {
|
||||||
const { date, time } = formatAuditDateParts(item.timestamp);
|
const { date, time } = formatAuditDateParts(item.timestamp);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -1070,6 +1111,12 @@ function ClientsPage() {
|
|||||||
<code className="rounded-md bg-secondary/60 px-2 py-1 font-mono text-xs text-muted-foreground">
|
<code className="rounded-md bg-secondary/60 px-2 py-1 font-mono text-xs text-muted-foreground">
|
||||||
{item.clientId}
|
{item.clientId}
|
||||||
</code>
|
</code>
|
||||||
|
<span className="font-semibold">
|
||||||
|
{item.actorName}
|
||||||
|
</span>
|
||||||
|
<code className="rounded-md bg-secondary/60 px-2 py-1 font-mono text-xs text-muted-foreground">
|
||||||
|
{item.actorId}
|
||||||
|
</code>
|
||||||
<Badge variant="muted">{item.actionLabel}</Badge>
|
<Badge variant="muted">{item.actionLabel}</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
|
|||||||
@@ -177,6 +177,27 @@ export type DevAssignableUserListResponse = {
|
|||||||
items: DevAssignableUser[];
|
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<string, unknown>;
|
||||||
|
department?: string;
|
||||||
|
grade?: string;
|
||||||
|
position?: string;
|
||||||
|
jobTitle?: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ConsentSummary = {
|
export type ConsentSummary = {
|
||||||
subject: string;
|
subject: string;
|
||||||
userName?: string;
|
userName?: string;
|
||||||
@@ -290,6 +311,11 @@ export async function fetchDevUsers(
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchDevUser(userId: string) {
|
||||||
|
const { data } = await apiClient.get<DevUserSummary>(`/admin/users/${userId}`);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
export async function addClientRelation(
|
export async function addClientRelation(
|
||||||
clientId: string,
|
clientId: string,
|
||||||
payload: ClientRelationUpsertRequest,
|
payload: ClientRelationUpsertRequest,
|
||||||
|
|||||||
Reference in New Issue
Block a user