@@ -0,0 +1,74 @@
|
||||
import { graphql, usePaginationFragment } from "react-relay";
|
||||
import type { SCIMEventListFragment$key } from "/__generated__/iam/SCIMEventListFragment.graphql";
|
||||
import type { SCIMEventListPaginationQuery } from "/__generated__/iam/SCIMEventListPaginationQuery.graphql";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Thead, Tbody, Tr, Th, Td } from "@probo/ui";
|
||||
import { SortableTable } from "/components/SortableTable";
|
||||
import { SCIMEventListItem } from "./SCIMEventListItem";
|
||||
|
||||
const SCIMEventListFragment = graphql`
|
||||
fragment SCIMEventListFragment on SCIMConfiguration
|
||||
@refetchable(queryName: "SCIMEventListPaginationQuery")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 20 }
|
||||
after: { type: "CursorKey", defaultValue: null }
|
||||
before: { type: "CursorKey", defaultValue: null }
|
||||
last: { type: "Int", defaultValue: null }
|
||||
) {
|
||||
events(first: $first, after: $after, last: $last, before: $before)
|
||||
@connection(key: "SCIMEventListFragment_events")
|
||||
@required(action: THROW) {
|
||||
edges @required(action: THROW) {
|
||||
node {
|
||||
id
|
||||
...SCIMEventListItemFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function SCIMEventList(props: { fKey: SCIMEventListFragment$key }) {
|
||||
const { fKey } = props;
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const eventsPagination = usePaginationFragment<
|
||||
SCIMEventListPaginationQuery,
|
||||
SCIMEventListFragment$key
|
||||
>(SCIMEventListFragment, fKey);
|
||||
|
||||
return (
|
||||
<SortableTable
|
||||
{...eventsPagination}
|
||||
refetch={() => {
|
||||
eventsPagination.refetch({}, { fetchPolicy: "network-only" });
|
||||
}}
|
||||
pageSize={20}
|
||||
>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Time")}</Th>
|
||||
<Th>{__("Method")}</Th>
|
||||
<Th>{__("Path")}</Th>
|
||||
<Th>{__("Status")}</Th>
|
||||
<Th>{__("User")}</Th>
|
||||
<Th>{__("IP Address")}</Th>
|
||||
<Th>{__("Error")}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{eventsPagination.data.events.edges.length === 0 ? (
|
||||
<Tr>
|
||||
<Td colSpan={7} className="text-center text-txt-secondary">
|
||||
{__("No SCIM events recorded yet.")}
|
||||
</Td>
|
||||
</Tr>
|
||||
) : (
|
||||
eventsPagination.data.events.edges.map(({ node: event }) => (
|
||||
<SCIMEventListItem key={event.id} fKey={event} />
|
||||
))
|
||||
)}
|
||||
</Tbody>
|
||||
</SortableTable>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useFragment } from "react-relay";
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { SCIMEventListItemFragment$key } from "/__generated__/iam/SCIMEventListItemFragment.graphql";
|
||||
import { Td, Tr, Badge } from "@probo/ui";
|
||||
import { formatDate } from "@probo/helpers";
|
||||
|
||||
const SCIMEventListItemFragment = graphql`
|
||||
fragment SCIMEventListItemFragment on SCIMEvent {
|
||||
id
|
||||
method
|
||||
path
|
||||
statusCode
|
||||
errorMessage
|
||||
ipAddress
|
||||
createdAt
|
||||
membership {
|
||||
id
|
||||
profile {
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const getStatusBadge = (statusCode: number) => {
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return <Badge variant="success">{statusCode}</Badge>;
|
||||
} else if (statusCode >= 400 && statusCode < 500) {
|
||||
return <Badge variant="warning">{statusCode}</Badge>;
|
||||
} else if (statusCode >= 500) {
|
||||
return <Badge variant="danger">{statusCode}</Badge>;
|
||||
}
|
||||
return <Badge variant="neutral">{statusCode}</Badge>;
|
||||
};
|
||||
|
||||
const getMethodBadge = (method: string) => {
|
||||
const variants: Record<string, "neutral" | "info" | "danger" | "highlight"> =
|
||||
{
|
||||
GET: "info",
|
||||
POST: "highlight",
|
||||
PUT: "highlight",
|
||||
PATCH: "highlight",
|
||||
DELETE: "danger",
|
||||
};
|
||||
return <Badge variant={variants[method] || "neutral"}>{method}</Badge>;
|
||||
};
|
||||
|
||||
export function SCIMEventListItem(props: {
|
||||
fKey: SCIMEventListItemFragment$key;
|
||||
}) {
|
||||
const { fKey } = props;
|
||||
|
||||
const event = useFragment<SCIMEventListItemFragment$key>(
|
||||
SCIMEventListItemFragment,
|
||||
fKey
|
||||
);
|
||||
|
||||
return (
|
||||
<Tr>
|
||||
<Td className="whitespace-nowrap">{formatDate(event.createdAt)}</Td>
|
||||
<Td>{getMethodBadge(event.method)}</Td>
|
||||
<Td className="font-mono text-xs">{event.path}</Td>
|
||||
<Td>{getStatusBadge(event.statusCode)}</Td>
|
||||
<Td>{event.membership?.profile?.fullName || "-"}</Td>
|
||||
<Td className="font-mono text-xs">{event.ipAddress}</Td>
|
||||
<Td className="max-w-xs truncate text-txt-danger">
|
||||
{event.errorMessage || "-"}
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user