Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"@bufbuild/protobuf": "^2.6.2",
"@connectrpc/connect": "^2.1.0",
"@connectrpc/connect-query": "^2.1.1",
"@connectrpc/connect-web": "^2.0.3",
"@hookform/resolvers": "^3.0.1",
Expand All @@ -22,6 +23,7 @@
"@raystack/proton": "^0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58",
"@stitches/react": "^1.2.8",
"@tanstack/react-query": "^5.83.0",
"@tanstack/react-query-devtools": "^5.90.2",
"@tanstack/react-table": "^8.9.3",
"@tanstack/table-core": "^8.21.3",
"axios": "^1.8.4",
Expand Down
424 changes: 175 additions & 249 deletions ui/pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions ui/src/assets/icons/cpu-chip.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ui/src/assets/images/service-user.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 14 additions & 14 deletions ui/src/components/Sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import PlansIcon from "~/assets/icons/plans.svg?react";
import WebhooksIcon from "~/assets/icons/webhooks.svg?react";
import PreferencesIcon from "~/assets/icons/preferences.svg?react";
import AdminsIcon from "~/assets/icons/admins.svg?react";
import CpuChipIcon from "~/assets/icons/cpu-chip.svg?react";
import { AppContext } from "~/contexts/App";
import { MoonIcon, SunIcon } from "@radix-ui/react-icons";
import { Link, useLocation } from "react-router-dom";
Expand All @@ -46,6 +47,11 @@ const navigationItems: NavigationItemsTypes[] = [
to: `/users`,
icon: <UserIcon />,
},
{
name: "Audit Logs",
to: `/audit-logs`,
icon: <CpuChipIcon />,
},
{
name: "Invoices",
to: `/invoices`,
Expand Down Expand Up @@ -132,21 +138,19 @@ export default function IAMSidebar() {
</Text>
</Sidebar.Header>
<Sidebar.Main>
{navigationItems.map((nav) => {
{navigationItems.map(nav => {
return nav?.subItems?.length ? (
<Sidebar.Group
label={nav.name}
key={nav.name}
className={styles["sidebar-group"]}
>
{nav.subItems?.map((subItem) => (
className={styles["sidebar-group"]}>
{nav.subItems?.map(subItem => (
<Sidebar.Item
leadingIcon={subItem.icon}
key={subItem.name}
active={isActive(subItem.to)}
data-test-id={`admin-ui-sidebar-navigation-cell-${subItem.name}`}
as={<Link to={subItem?.to ?? ""} />}
>
as={<Link to={subItem?.to ?? ""} />}>
{subItem.name}
</Sidebar.Item>
))}
Expand All @@ -157,8 +161,7 @@ export default function IAMSidebar() {
key={nav.name}
active={isActive(nav.to)}
data-test-id={`admin-ui-sidebar-navigation-cell-${nav.name}`}
as={<Link to={nav?.to ?? ""} />}
>
as={<Link to={nav?.to ?? ""} />}>
{nav.name}
</Sidebar.Item>
);
Expand Down Expand Up @@ -203,22 +206,19 @@ function UserDropdown() {
leadingIcon={
<Avatar src={user?.avatar} fallback={userInital} size={3} />
}
data-test-id="frontier-sdk-sidebar-logout"
>
data-test-id="frontier-sdk-sidebar-logout">
{user?.email}
</Sidebar.Item>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item
onClick={toggleTheme}
data-test-id="admin-ui-toggle-theme"
>
data-test-id="admin-ui-toggle-theme">
{themeData.icon} {themeData.label}
</DropdownMenu.Item>
<DropdownMenu.Item
onClick={() => logoutMutation.mutate({})}
data-test-id="admin-ui-logout-btn"
>
data-test-id="admin-ui-logout-btn">
Logout
</DropdownMenu.Item>
</DropdownMenu.Content>
Expand Down
6 changes: 3 additions & 3 deletions ui/src/contexts/ConnectProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import type { ReactNode } from "react";
import { TransportProvider } from "@connectrpc/connect-query";
import { jsonTransport as transport } from "~/connect/transport";
Expand All @@ -20,9 +21,8 @@ interface ConnectProviderProps {
export function ConnectProvider({ children }: ConnectProviderProps) {
return (
<QueryClientProvider client={queryClient}>
<TransportProvider transport={transport}>
{children}
</TransportProvider>
<TransportProvider transport={transport}>{children}</TransportProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
Expand Down
131 changes: 131 additions & 0 deletions ui/src/pages/audit-logs/list/columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import {
Avatar,
Badge,
DataTableColumnDef,
Flex,
getAvatarColor,
Text,
} from "@raystack/apsara";
import dayjs from "dayjs";
import styles from "./list.module.css";
import {
AuditRecord,
AuditRecordActor,
AuditRecordResource,
} from "@raystack/proton/frontier";
import {
isNullTimestamp,
TimeStamp,
timestampToDate,
} from "~/utils/connect-timestamp";
import {
getActionBadgeColor,
getAuditLogActorName,
isAuditLogActorServiceUser,
} from "../util";
import serviceUserIcon from "~/assets/images/service-user.jpg";
import { OrganizationCell } from "./organization-cell";
import { ComponentPropsWithoutRef } from "react";

interface getColumnsOptions {
groupCountMap: Record<string, Record<string, number>>;
}

export const getColumns = ({
groupCountMap,
}: getColumnsOptions): DataTableColumnDef<AuditRecord, unknown>[] => {
return [
{
accessorKey: "actor",
header: "Actor",
classNames: {
cell: styles["name-column"],
header: styles["name-column"],
},
cell: ({ getValue }) => {
const value = getValue() as AuditRecordActor;
const name = getAuditLogActorName(value);
const isServiceUser = isAuditLogActorServiceUser(value);

return (
<Flex gap={4} align="center">
<Avatar
size={3}
fallback={name?.[0]?.toUpperCase()}
color={getAvatarColor(value?.id ?? "")}
radius="full"
src={isServiceUser ? serviceUserIcon : undefined}
/>
<Text size="regular">{name}</Text>
</Flex>
);
},
},
{
accessorKey: "orgId",
header: "Organization",
classNames: {
cell: styles["org-column"],
header: styles["org-column"],
},
cell: ({ getValue }) => {
return <OrganizationCell id={getValue() as string} />;
},
enableColumnFilter: true,
},
{
accessorKey: "event",
header: "Action",
cell: ({ getValue }) => {
const value = getValue() as string;
const color = getActionBadgeColor(value) as ComponentPropsWithoutRef<
typeof Badge
>["variant"];
return <Badge variant={color}>{value}</Badge>;
},
enableColumnFilter: true,
enableSorting: true,
},
{
accessorKey: "resource",
header: "Resource",
cell: ({ getValue }) => {
const value = getValue() as AuditRecordResource;
return (
<Flex gap={1} direction="column" className={styles.capitalize}>
<Text size="small" weight="medium">
{value.name}
</Text>
<Text size="small" variant="secondary">
{value.type.toLowerCase()}
</Text>
</Flex>
);
},
},
{
accessorKey: "occurredAt",
header: "Timestamp",
filterType: "date",
cell: ({ getValue }) => {
const value = getValue() as TimeStamp;
if (isNullTimestamp(value)) {
return <Text>-</Text>;
}
const date = dayjs(timestampToDate(value));
return (
<Flex gap={1} direction="column">
<Text size="small" weight="medium">
{date.format("DD MMM YYYY")}
</Text>
<Text size="small" variant="secondary">
{date.format("hh:mm A")}
</Text>
</Flex>
);
},
enableHiding: true,
enableSorting: true,
},
];
};
1 change: 1 addition & 0 deletions ui/src/pages/audit-logs/list/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AuditLogsList } from "./list";
63 changes: 63 additions & 0 deletions ui/src/pages/audit-logs/list/list.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.navbar {
padding: var(--rs-space-4) var(--rs-space-7);
border-bottom: 0.5px solid var(--rs-color-border-base-primary);
background: var(--rs-color-background-base-primary);
display: flex;
align-items: center;
justify-content: space-between;
}

.table {
height: auto;
}

.table-empty {
height: 100%;
}

.empty-state {
height: 100%;
}

.empty-state-subheading {
max-width: 360px;
text-wrap: auto;
}

.name-column {
padding-left: var(--rs-space-7);
max-width: 200px;
}
.org-column {
max-width: 200px;
}

.country-column {
max-width: 150px;
}

.table-wrapper {
flex: 1;
height: 100%;
}

.table-header {
z-index: 2;
}

.side-panel {
position: sticky;
top: 0;
}

.capitalize {
text-transform: capitalize;
}

.table-content-container {
width: 100%;
height: 100%;
max-height: calc(100vh - 90px);
overflow: scroll;
position: relative;
}
Loading