Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
19 changes: 19 additions & 0 deletions web/apps/admin/src/pages/audit-logs/AuditLogsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { AuditLogsView } from "@raystack/frontier/admin";
import { useCallback } from "react";
import { clients } from "~/connect/clients";
import { exportCsvFromStream } from "~/utils/helper";
import type { RQLExportRequest, RQLRequest } from "@raystack/proton/frontier";

const adminClient = clients.admin({ useBinary: true });

export function AuditLogsPage() {
const onExportCsv = useCallback(async (query: RQLRequest) => {
await exportCsvFromStream(
adminClient.exportAuditRecords,
{ query: query as unknown as RQLExportRequest },
"audit-logs.csv",
);
}, []);

return <AuditLogsView onExportCsv={onExportCsv} />;
}
1 change: 0 additions & 1 deletion web/apps/admin/src/pages/audit-logs/list/index.ts

This file was deleted.

4 changes: 2 additions & 2 deletions web/apps/admin/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { UserDetails } from "./pages/users/details";
import { UserDetailsSecurityPage } from "./pages/users/details/security";

import { InvoicesPage } from "./pages/invoices/InvoicesPage";
import { AuditLogsList } from "./pages/audit-logs/list";
import { AuditLogsPage } from "./pages/audit-logs/AuditLogsPage";

export default memo(function AppRoutes() {
const { isAdmin, isLoading, user } = useContext(AppContext);
Expand Down Expand Up @@ -82,7 +82,7 @@ export default memo(function AppRoutes() {
<Route path="security" element={<UserDetailsSecurityPage />} />
</Route>

<Route path="audit-logs" element={<AuditLogsList />} />
<Route path="audit-logs" element={<AuditLogsPage />} />

<Route path="plans" element={<PlanList />}>
<Route path=":planId" element={<PlanDetails />} />
Expand Down
22 changes: 22 additions & 0 deletions web/lib/admin/assets/icons/CpuChipIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { SVGProps } from "react";

export function CpuChipIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
d="M5.5 2V3M3 5.5H2M14 5.5H13M3 8H2M14 8H13M3 10.5H2M14 10.5H13M5.5 13V14M8 2V3M8 13V14M10.5 2V3M10.5 13V14M4.5 13H11.5C11.8978 13 12.2794 12.842 12.5607 12.5607C12.842 12.2794 13 11.8978 13 11.5V4.5C13 4.10218 12.842 3.72064 12.5607 3.43934C12.2794 3.15804 11.8978 3 11.5 3H4.5C4.10218 3 3.72064 3.15804 3.43934 3.43934C3.15804 3.72064 3 4.10218 3 4.5V11.5C3 11.8978 3.15804 12.2794 3.43934 12.5607C3.72064 12.842 4.10218 13 4.5 13ZM5 5H11V11H5V5Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
39 changes: 39 additions & 0 deletions web/lib/admin/assets/icons/JsonIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { SVGProps } from "react";

export function JsonIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
d="M9.99996 1.33337H3.99996C3.64634 1.33337 3.3072 1.47385 3.05715 1.7239C2.8071 1.97395 2.66663 2.31309 2.66663 2.66671V13.3334C2.66663 13.687 2.8071 14.0261 3.05715 14.2762C3.3072 14.5262 3.64634 14.6667 3.99996 14.6667H12C12.3536 14.6667 12.6927 14.5262 12.9428 14.2762C13.1928 14.0261 13.3333 13.687 13.3333 13.3334V4.66671L9.99996 1.33337Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M9.33337 1.33337V4.00004C9.33337 4.35366 9.47385 4.6928 9.7239 4.94285C9.97395 5.1929 10.3131 5.33337 10.6667 5.33337H13.3334"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M6.66671 8C6.4899 8 6.32033 8.07024 6.1953 8.19526C6.07028 8.32029 6.00004 8.48986 6.00004 8.66667V9.33333C6.00004 9.51014 5.9298 9.67971 5.80478 9.80474C5.67975 9.92976 5.51019 10 5.33337 10C5.51019 10 5.67975 10.0702 5.80478 10.1953C5.9298 10.3203 6.00004 10.4899 6.00004 10.6667V11.3333C6.00004 11.5101 6.07028 11.6797 6.1953 11.8047C6.32033 11.9298 6.4899 12 6.66671 12"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M9.33337 12C9.51019 12 9.67975 11.9298 9.80478 11.8047C9.9298 11.6797 10 11.5101 10 11.3333V10.6667C10 10.4899 10.0703 10.3203 10.1953 10.1953C10.3203 10.0702 10.4899 10 10.6667 10C10.4899 10 10.3203 9.92976 10.1953 9.80474C10.0703 9.67971 10 9.51014 10 9.33333V8.66667C10 8.48986 9.9298 8.32029 9.80478 8.19526C9.67975 8.07024 9.51019 8 9.33337 8"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
21 changes: 21 additions & 0 deletions web/lib/admin/assets/icons/KeyIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { SVGProps } from "react";

export function KeyIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
d="M10.5 3.5C11.6046 3.5 12.5 4.39543 12.5 5.5M14.5 5.5C14.5 7.70914 12.7091 9.5 10.5 9.5C10.2662 9.5 10.037 9.47994 9.8142 9.44144C9.43885 9.37658 9.04134 9.45866 8.772 9.728L7 11.5H5.5V13H4V14.5H1.5V12.6213C1.5 12.2235 1.65804 11.842 1.93934 11.5607L6.272 7.228C6.54134 6.95866 6.62342 6.56115 6.55856 6.1858C6.52006 5.96297 6.5 5.73383 6.5 5.5C6.5 3.29086 8.29086 1.5 10.5 1.5C12.7091 1.5 14.5 3.29086 14.5 5.5Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
21 changes: 21 additions & 0 deletions web/lib/admin/assets/icons/MapIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { SVGProps } from "react";

export function MapIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
d="M6 4.50001V10M10 6.00001V11.5M10.3353 13.832L13.5853 12.2073C13.8393 12.0807 14 11.8207 14 11.5367V3.21334C14 2.65601 13.4133 2.29334 12.9147 2.54267L10.3353 3.83201C10.124 3.93801 9.87533 3.93801 9.66467 3.83201L6.33533 2.16801C6.23121 2.11596 6.1164 2.08887 6 2.08887C5.8836 2.08887 5.76879 2.11596 5.66467 2.16801L2.41467 3.79268C2.16 3.92001 2 4.18001 2 4.46334V12.7867C2 13.344 2.58667 13.7067 3.08533 13.4573L5.66467 12.168C5.876 12.062 6.12467 12.062 6.33533 12.168L9.66467 13.8327C9.876 13.938 10.1247 13.938 10.3353 13.8327V13.832Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
Binary file added web/lib/admin/assets/images/system.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions web/lib/admin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
export { default as RolesView } from "./views/roles";
export { default as InvoicesView } from "./views/invoices";
export { ProductsView, ProductPricesView } from "./views/products/exports";
export { default as AuditLogsView } from "./views/audit-logs";

// utils exports
export {
Expand Down
7 changes: 7 additions & 0 deletions web/lib/admin/utils/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,10 @@ export function reduceByKey<T extends Record<string, unknown>>(
{} as Record<string, T>
);
}

const ZERO_UUID = "00000000-0000-0000-0000-000000000000" as const;

export function isZeroUUID(uuid: string) {
if (typeof uuid !== "string") return false;
return uuid.toLowerCase() === ZERO_UUID;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Avatar, Flex, getAvatarColor, Text } from "@raystack/apsara";
import { AuditRecordActor } from "@raystack/proton/frontier";
import { ACTOR_TYPES, getAuditLogActorName } from "../util";
import systemIcon from "~/assets/images/system.jpg";
import KeyIcon from "~/assets/icons/key.svg?react";
import { ACTOR_TYPES, getAuditLogActorName } from "./util";
import systemIcon from "../../assets/images/system.jpg";
import { KeyIcon } from "../../assets/icons/KeyIcon";

type ActorCellProps = {
size?: "large" | "small";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Badge, DataTableColumnDef, Flex, Text } from "@raystack/apsara";
import dayjs from "dayjs";
import styles from "./list.module.css";
import styles from "./audit-logs.module.css";
import {
AuditRecord,
AuditRecordActor,
Expand All @@ -10,8 +10,8 @@
isNullTimestamp,
TimeStamp,
timestampToDate,
} from "~/utils/connect-timestamp";
import { ACTOR_TYPES, getActionBadgeColor } from "../util";
} from "../../utils/connect-timestamp";
import { ACTOR_TYPES, getActionBadgeColor } from "./util";
import { ComponentPropsWithoutRef } from "react";
import ActorCell from "./actor-cell";

Expand All @@ -20,7 +20,7 @@
}

export const getColumns = ({
groupCountMap,

Check warning on line 23 in web/lib/admin/views/audit-logs/columns.tsx

View workflow job for this annotation

GitHub Actions / JS SDK Lint

'groupCountMap' is defined but never used
}: getColumnsOptions): DataTableColumnDef<AuditRecord, unknown>[] => {
return [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import { useDebouncedState } from "@raystack/apsara/hooks";
import { useCallback, useMemo, useState } from "react";
import Navbar from "./navbar";
import styles from "./list.module.css";
import styles from "./audit-logs.module.css";
import { getColumns } from "./columns";
import PageTitle from "~/components/page-title";
import CpuChipIcon from "~/assets/icons/cpu-chip.svg?react";
import { PageTitle } from "../../components/PageTitle";
import { CpuChipIcon } from "../../assets/icons/CpuChipIcon";
import { useInfiniteQuery } from "@connectrpc/connect-query";
import {
AdminServiceQueries,
Expand All @@ -22,12 +22,12 @@
getConnectNextPageParam,
getGroupCountMapFromFirstPage,
DEFAULT_PAGE_SIZE,
transformDataTableQueryToRQLRequest,
} from "@raystack/frontier/admin";
} from "../../utils/connect-pagination";
import { transformDataTableQueryToRQLRequest } from "../../utils/transform-query";
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
import SidePanelDetails from "./sidepanel-details";
import { useQueryClient } from "@tanstack/react-query";
import { AUDIT_LOG_QUERY_KEY } from "../util";
import { AUDIT_LOG_QUERY_KEY } from "./util";

const NoAuditLogs = () => {
return (
Expand Down Expand Up @@ -60,7 +60,12 @@
},
};

export const AuditLogsList = () => {
export type AuditLogsViewProps = {
appName?: string;
onExportCsv?: (query: RQLRequest) => Promise<void>;

Check warning on line 65 in web/lib/admin/views/audit-logs/index.tsx

View workflow job for this annotation

GitHub Actions / JS SDK Lint

'query' is defined but never used
};

export default function AuditLogsView({ appName, onExportCsv }: AuditLogsViewProps = {}) {
const queryClient = useQueryClient();
const [tableQuery, setTableQuery] = useDebouncedState<{
query: DataTableQuery;
Expand Down Expand Up @@ -126,7 +131,7 @@
rqlRequest: updatedRQLRequest,
});
},
[queryClient],

Check warning on line 134 in web/lib/admin/views/audit-logs/index.tsx

View workflow job for this annotation

GitHub Actions / JS SDK Lint

React Hook useCallback has a missing dependency: 'setTableQuery'. Either include it or remove the dependency array
);

const handleLoadMore = async () => {
Expand Down Expand Up @@ -162,7 +167,7 @@
console.error("ConnectRPC Error:", error);
return (
<>
<PageTitle title="Audit Logs" />
<PageTitle title="Audit Logs" appName={appName} />
<EmptyState
icon={<ExclamationTriangleIcon />}
heading="Error Loading Audit Logs"
Expand All @@ -180,7 +185,7 @@

return (
<>
<PageTitle title="Audit Logs" />
<PageTitle title="Audit Logs" appName={appName} />
<DataTable
query={tableQuery.query}
columns={columns}
Expand All @@ -192,7 +197,7 @@
onLoadMore={handleLoadMore}
onRowClick={onRowClick}>
<Flex direction="column" style={{ width: "100%" }}>
<Navbar searchQuery={tableQuery.query.search} />
<Navbar searchQuery={tableQuery.query.search} onExportCsv={onExportCsv} />
<DataTable.Toolbar />
<Flex className={styles["table-content-container"]}>
<DataTable.Content
Expand All @@ -214,4 +219,4 @@
</DataTable>
</>
);
};
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { DataTable, Flex, Text, IconButton, Spinner } from "@raystack/apsara";
import CpuChipIcon from "~/assets/icons/cpu-chip.svg?react";
import styles from "./list.module.css";
import { CpuChipIcon } from "../../assets/icons/CpuChipIcon";
import styles from "./audit-logs.module.css";
import { DownloadIcon, MagnifyingGlassIcon } from "@radix-ui/react-icons";
import React, { useCallback, useState } from "react";
import { clients } from "~/connect/clients";
import { exportCsvFromStream } from "~/utils/helper";
import { useQueryClient } from "@tanstack/react-query";
import { AUDIT_LOG_QUERY_KEY } from "../util";
import { RQLExportRequest } from "@raystack/proton/frontier";

const adminClient = clients.admin({ useBinary: true });
import { AUDIT_LOG_QUERY_KEY } from "./util";
import { RQLRequest } from "@raystack/proton/frontier";

interface NavbarProps {
searchQuery?: string;
onExportCsv?: (query: RQLRequest) => Promise<void>;

Check warning on line 12 in web/lib/admin/views/audit-logs/navbar.tsx

View workflow job for this annotation

GitHub Actions / JS SDK Lint

'query' is defined but never used
}

const Navbar = ({ searchQuery }: NavbarProps) => {
const Navbar = ({ searchQuery, onExportCsv }: NavbarProps) => {
const [showSearch, setShowSearch] = useState(searchQuery ? true : false);
const [isDownloading, setIsDownloading] = useState(false);
const queryClient = useQueryClient();
Expand All @@ -32,23 +29,19 @@
}, []);

const onDownloadClick = useCallback(async () => {
if (!onExportCsv) return;
try {
setIsDownloading(true);
const query = queryClient.getQueryData(
AUDIT_LOG_QUERY_KEY,
) as RQLExportRequest;

await exportCsvFromStream(
adminClient.exportAuditRecords,
{ query },
"audit-logs.csv",
);
) as RQLRequest;
await onExportCsv(query);
} catch (error) {
console.error(error);
} finally {
setIsDownloading(false);
}
}, [queryClient]);
}, [queryClient, onExportCsv]);

return (
<nav className={styles.navbar}>
Expand All @@ -75,6 +68,7 @@
<MagnifyingGlassIcon />
</IconButton>
)}
{onExportCsv && (
<IconButton
size={3}
aria-label="Download"
Expand All @@ -83,6 +77,7 @@
disabled={isDownloading}>
{isDownloading ? <Spinner /> : <DownloadIcon />}
</IconButton>
)}
</Flex>
</nav>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import {
TransformIcon,
} from "@radix-ui/react-icons";
import { List } from "@raystack/apsara";
import styles from "./list.module.css";
import styles from "./audit-logs.module.css";
import { AuditRecord } from "@raystack/proton/frontier";
import { ACTOR_TYPES } from "../util";
import { timestampToDate } from "~/utils/connect-timestamp";
import { ACTOR_TYPES } from "./util";
import { timestampToDate } from "../../utils/connect-timestamp";
import dayjs from "dayjs";
import MapIcon from "~/assets/icons/map.svg?react";
import { MapIcon } from "../../assets/icons/MapIcon";
import SidePanelLogDialog from "./sidepanel-log-dialog";
import ActorCell from "./actor-cell";
import SidepanelListItemLink from "./sidepanel-list-link";
import { isZeroUUID } from "~/utils/helper";
import { isZeroUUID } from "../../utils/helper";
import SidepanelListId from "./sidepanel-list-id";

type SidePanelDetailsProps = Partial<AuditRecord> & {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CopyButton, Flex, List, Text, Tooltip } from "@raystack/apsara";
import styles from "./list.module.css";
import styles from "./audit-logs.module.css";

export default function SidepanelListId({ id = "-" }: { id?: string }) {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button, List } from "@raystack/apsara";
import { ReactNode } from "react";
import { Link } from "react-router-dom";
import styles from "./list.module.css";
import styles from "./audit-logs.module.css";

type SidepanelListItemLinkProps = {
isLink: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Dialog, IconButton, CodeBlock } from "@raystack/apsara";
import styles from "./list.module.css";
import styles from "./audit-logs.module.css";
import { AuditRecord } from "@raystack/proton/frontier";
import { auditLogToJson } from "../util";
import JsonIcon from "~/assets/icons/json.svg?react";
import { auditLogToJson } from "./util";
import { JsonIcon } from "../../assets/icons/JsonIcon";

export default function SidePanelLogDialog(props: Partial<AuditRecord>) {
return (
Expand Down
Loading
Loading