Improve scim event UI/UX
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -50,16 +50,13 @@ export function SCIMEventList(props: { fKey: SCIMEventListFragment$key }) {
|
||||
<Th>{__("Time")}</Th>
|
||||
<Th>{__("Method")}</Th>
|
||||
<Th>{__("Path")}</Th>
|
||||
<Th>{__("Status")}</Th>
|
||||
<Th>{__("User")}</Th>
|
||||
<Th>{__("IP Address")}</Th>
|
||||
<Th>{__("Error")}</Th>
|
||||
<Th>{__("Result")}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{eventsPagination.data.events.edges.length === 0 ? (
|
||||
<Tr>
|
||||
<Td colSpan={7} className="text-center text-txt-secondary">
|
||||
<Td colSpan={4} className="text-center text-txt-secondary">
|
||||
{__("No SCIM events recorded yet.")}
|
||||
</Td>
|
||||
</Tr>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useState } from "react";
|
||||
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 { Td, Tr, Badge, IconChevronDown, IconChevronRight } from "@probo/ui";
|
||||
import { formatDate } from "@probo/helpers";
|
||||
|
||||
const SCIMEventListItemFragment = graphql`
|
||||
@@ -22,15 +23,11 @@ const SCIMEventListItemFragment = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const getStatusBadge = (statusCode: number) => {
|
||||
const getResultBadge = (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="success">Info</Badge>;
|
||||
}
|
||||
return <Badge variant="neutral">{statusCode}</Badge>;
|
||||
return <Badge variant="danger">Error</Badge>;
|
||||
};
|
||||
|
||||
const getMethodBadge = (method: string) => {
|
||||
@@ -45,27 +42,77 @@ const getMethodBadge = (method: string) => {
|
||||
return <Badge variant={variants[method] || "neutral"}>{method}</Badge>;
|
||||
};
|
||||
|
||||
const decodePath = (path: string): string => {
|
||||
try {
|
||||
return decodeURIComponent(path);
|
||||
} catch {
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
export function SCIMEventListItem(props: {
|
||||
fKey: SCIMEventListItemFragment$key;
|
||||
}) {
|
||||
const { fKey } = props;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const event = useFragment<SCIMEventListItemFragment$key>(
|
||||
SCIMEventListItemFragment,
|
||||
fKey
|
||||
);
|
||||
|
||||
const hasError = !!event.errorMessage;
|
||||
|
||||
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>
|
||||
<>
|
||||
<Tr
|
||||
className="cursor-pointer hover:bg-bg-hover"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
>
|
||||
<Td className="whitespace-nowrap">
|
||||
<div className="flex items-center gap-2">
|
||||
{isExpanded ? (
|
||||
<IconChevronDown size={16} className="text-txt-secondary" />
|
||||
) : (
|
||||
<IconChevronRight size={16} className="text-txt-secondary" />
|
||||
)}
|
||||
{formatDate(event.createdAt)}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>{getMethodBadge(event.method)}</Td>
|
||||
<Td className="font-mono text-xs">{decodePath(event.path)}</Td>
|
||||
<Td>{getResultBadge(event.statusCode)}</Td>
|
||||
</Tr>
|
||||
{isExpanded && (
|
||||
<Tr>
|
||||
<Td colSpan={4} className="bg-bg-subtle">
|
||||
<div className="py-2 pl-6 space-y-2">
|
||||
<div className="flex gap-8 text-sm">
|
||||
<div>
|
||||
<span className="text-txt-secondary">User: </span>
|
||||
<span>{event.membership?.profile?.fullName || "-"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-txt-secondary">IP Address: </span>
|
||||
<span className="font-mono text-xs">{event.ipAddress}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-txt-secondary">Status Code: </span>
|
||||
<span className="font-mono text-xs">{event.statusCode}</span>
|
||||
</div>
|
||||
</div>
|
||||
{hasError && (
|
||||
<div>
|
||||
<div className="text-sm text-txt-secondary">Error</div>
|
||||
<pre className="mt-1 whitespace-pre-wrap break-all font-mono text-xs text-txt-danger">
|
||||
{event.errorMessage}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user