From c6e6436f342242ad370b90138e2c04fdc3eccecb Mon Sep 17 00:00:00 2001 From: William Wills Date: Tue, 2 Apr 2024 14:15:43 -0400 Subject: [PATCH 1/2] feat: added audit table feat: added audit api feat: added audit page chore: renamed ProjectsList and UnitList to ProjectsListPage and UnitListPage respectively chore: corrected route constants --- src/renderer/api/cadt/v1/audit/audit.api.ts | 46 +++++++ src/renderer/api/cadt/v1/audit/index.ts | 1 + src/renderer/api/cadt/v1/index.ts | 5 +- src/renderer/api/index.ts | 3 +- .../components/blocks/layout/LeftNav.tsx | 7 ++ .../components/blocks/tables/AuditsTable.tsx | 101 +++++++++++++++ .../components/blocks/tables/index.ts | 3 +- src/renderer/pages/Audit/AuditPage.tsx | 116 ++++++++++++++++++ src/renderer/pages/Audit/index.ts | 1 + ...{ProjectsList.tsx => ProjectsListPage.tsx} | 10 +- src/renderer/pages/ProjectsList/index.ts | 2 +- .../{UnitsList.tsx => UnitsListPage.tsx} | 10 +- src/renderer/pages/UnitsList/index.ts | 2 +- src/renderer/pages/index.ts | 1 + src/renderer/routes/AppNavigator.tsx | 8 +- src/renderer/routes/route-constants.ts | 3 +- src/renderer/translations/tokens/en-US.json | 12 +- 17 files changed, 310 insertions(+), 21 deletions(-) create mode 100644 src/renderer/api/cadt/v1/audit/audit.api.ts create mode 100644 src/renderer/api/cadt/v1/audit/index.ts create mode 100644 src/renderer/components/blocks/tables/AuditsTable.tsx create mode 100644 src/renderer/pages/Audit/AuditPage.tsx create mode 100644 src/renderer/pages/Audit/index.ts rename src/renderer/pages/ProjectsList/{ProjectsList.tsx => ProjectsListPage.tsx} (90%) rename src/renderer/pages/UnitsList/{UnitsList.tsx => UnitsListPage.tsx} (90%) diff --git a/src/renderer/api/cadt/v1/audit/audit.api.ts b/src/renderer/api/cadt/v1/audit/audit.api.ts new file mode 100644 index 00000000..7f405e01 --- /dev/null +++ b/src/renderer/api/cadt/v1/audit/audit.api.ts @@ -0,0 +1,46 @@ +import {cadtApi, auditTag} from "../"; + +const host: string = 'http://localhost:31310' + +interface GetAuditParams { + page: number; + orgUid: string; + search?: string; + order?: string; +} + +interface GetAuditResponse { + page: number, + pageCount: number, + data: any[] +} + +const auditApi = cadtApi.injectEndpoints({ + endpoints: (builder) => ({ + getAudit: builder.query({ + query: ({ page, orgUid, search, order }: GetAuditParams) => { + // Initialize the params object with page and limit + const params: GetAuditParams & {limit: number} = { orgUid, page, limit: 10 }; + + if (search) { + params.search = search.replace(/[^a-zA-Z0-9 _.-]+/, ''); + } + + if (order) { + params.order = order; + } + + return { + url: `${host}/v1/audit`, + params, // Use the constructed params object + method: 'GET', + }; + }, + providesTags: (_response, _error, {orgUid}) => [{type: auditTag, id: orgUid}], + }) + }) +}); + +export const { + useGetAuditQuery +} = auditApi; \ No newline at end of file diff --git a/src/renderer/api/cadt/v1/audit/index.ts b/src/renderer/api/cadt/v1/audit/index.ts new file mode 100644 index 00000000..0a3a334d --- /dev/null +++ b/src/renderer/api/cadt/v1/audit/index.ts @@ -0,0 +1 @@ +export * from './audit.api'; \ No newline at end of file diff --git a/src/renderer/api/cadt/v1/index.ts b/src/renderer/api/cadt/v1/index.ts index 3f949ea3..f4b62c4e 100644 --- a/src/renderer/api/cadt/v1/index.ts +++ b/src/renderer/api/cadt/v1/index.ts @@ -3,16 +3,17 @@ import {createApi, fetchBaseQuery} from '@reduxjs/toolkit/query/react'; const projectsTag: string = 'projects'; const organizationsTag: string = 'organizations'; const unitsTag: string = 'projects'; +const auditTag: string = 'audit'; const baseQuery = fetchBaseQuery({ baseUrl: '/', }); -export { projectsTag, organizationsTag, unitsTag}; +export { projectsTag, organizationsTag, unitsTag, auditTag}; export const cadtApi = createApi({ baseQuery, reducerPath: 'cadtApi', - tagTypes: [projectsTag, organizationsTag, unitsTag], + tagTypes: [projectsTag, organizationsTag, unitsTag, auditTag], endpoints: () => ({}), }); diff --git a/src/renderer/api/index.ts b/src/renderer/api/index.ts index acbe8d0e..84e492e1 100644 --- a/src/renderer/api/index.ts +++ b/src/renderer/api/index.ts @@ -1,3 +1,4 @@ export * from './cadt/v1/organizations'; export * from './cadt/v1/units'; -export * from './cadt/v1/projects'; \ No newline at end of file +export * from './cadt/v1/projects'; +export * from './cadt/v1/audit'; \ No newline at end of file diff --git a/src/renderer/components/blocks/layout/LeftNav.tsx b/src/renderer/components/blocks/layout/LeftNav.tsx index 8917305e..b95e7c65 100644 --- a/src/renderer/components/blocks/layout/LeftNav.tsx +++ b/src/renderer/components/blocks/layout/LeftNav.tsx @@ -64,6 +64,13 @@ const LeftNav = () => { > + navigate(ROUTES.AUDIT)} + > + + {/* Add more Sidebar.Item as needed */} diff --git a/src/renderer/components/blocks/tables/AuditsTable.tsx b/src/renderer/components/blocks/tables/AuditsTable.tsx new file mode 100644 index 00000000..bbb62f13 --- /dev/null +++ b/src/renderer/components/blocks/tables/AuditsTable.tsx @@ -0,0 +1,101 @@ +import React, { useMemo } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { DebouncedFunc } from 'lodash'; +import { DataTable, PageCounter, Pagination } from '@/components'; + +interface TableProps { + data: any[]; + isLoading: boolean; + currentPage: number; + onPageChange: DebouncedFunc<(page: any) => void>; + setOrder?: (sort: string) => void; + order?: string; + totalPages: number; + totalCount: number; +} + +const AuditsTable: React.FC = ({ + data, + isLoading, + currentPage, + onPageChange, + setOrder, + order, + totalPages, + totalCount, +}) => { + + + const columns = useMemo( + () => [ + { + title: , + key: 'table', + }, + { + title: , + key: 'onchainConfirmationTimeStamp', + render: (row: any) => { + return ( + <> + { + new Intl.DateTimeFormat('en-US', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }).format(new Date(row.onchainConfirmationTimeStamp * 1000)) + } + + ); + } + }, + { + title: , + key: 'type', + }, + { + title: , + key: 'rootHash', + }, + { + title: , + key: 'author', + }, + { + title: , + key: 'comment', + }, + ], + [], + ); + + return ( +
+ + + + + } + /> +
+ ); +}; + +export { AuditsTable }; diff --git a/src/renderer/components/blocks/tables/index.ts b/src/renderer/components/blocks/tables/index.ts index 5574dbd8..12ab858e 100644 --- a/src/renderer/components/blocks/tables/index.ts +++ b/src/renderer/components/blocks/tables/index.ts @@ -1,3 +1,4 @@ export * from './ProjectsListTable'; export * from './SkeletonTable'; -export * from './UnitsListTable'; \ No newline at end of file +export * from './UnitsListTable'; +export * from './AuditsTable'; \ No newline at end of file diff --git a/src/renderer/pages/Audit/AuditPage.tsx b/src/renderer/pages/Audit/AuditPage.tsx new file mode 100644 index 00000000..563de953 --- /dev/null +++ b/src/renderer/pages/Audit/AuditPage.tsx @@ -0,0 +1,116 @@ +import React, { useCallback } from 'react'; +import { useGetAuditQuery } from '@/api'; +import { useQueryParamState } from '@/hooks'; +import {debounce, DebouncedFunc} from 'lodash'; +import { + OrganizationSelector, + IndeterminateProgressOverlay, + SkeletonTable, + AuditsTable, + SearchBox, +} from '@/components'; +import {FormattedMessage} from "react-intl"; + +const AuditPage: React.FC = () => { + const [currentPage, setCurrentPage] = useQueryParamState('page', '1'); + const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined); + const [search, setSearch] = useQueryParamState('search', undefined); + const [order, setOrder] = useQueryParamState('order', undefined); + + const { + data: auditData, + isLoading: auditLoading, + isFetching: auditFetching, + error: auditError, + } = useGetAuditQuery({ page: Number(currentPage), orgUid, search, order }, {skip: !orgUid}); + + const handlePageChange: DebouncedFunc = useCallback( + debounce((page) => setCurrentPage(page), 800), + [setCurrentPage], + ); + + const handleOrganizationSelected = useCallback( + (organization: any) => { + setOrgUid(organization?.orgUid); + }, + [setOrgUid], + ); + + const handleSearchChange: DebouncedFunc = useCallback( + debounce((event: any) => { + setSearch(event.target.value); + }, 800), + [setSearch, debounce], + ); + + const handleSetOrder = useCallback(() => { + const currentDirection = order; + + // Cycle through 'ASC', 'DESC', and no order ('') + switch (currentDirection) { + case 'ASC': + setOrder(`DESC`); + break; + case 'DESC': + setOrder(''); + break; + default: + setOrder(`ASC`); + break; + } + + }, [order, setOrder]); + + if (!orgUid){ + return ( + <> +
+ +
+
+ +
+ + ); + } + + if (auditLoading) { + return ; + } + + if (auditError) { + return ; + } + + if (!auditData) { + return ; + } + + return ( + <> + {auditFetching && } +
+ + +
+ <> + {auditLoading ? ( + + ) : ( + + )} + + + ); +}; + +export {AuditPage}; diff --git a/src/renderer/pages/Audit/index.ts b/src/renderer/pages/Audit/index.ts new file mode 100644 index 00000000..25f5689f --- /dev/null +++ b/src/renderer/pages/Audit/index.ts @@ -0,0 +1 @@ +export * from './AuditPage'; \ No newline at end of file diff --git a/src/renderer/pages/ProjectsList/ProjectsList.tsx b/src/renderer/pages/ProjectsList/ProjectsListPage.tsx similarity index 90% rename from src/renderer/pages/ProjectsList/ProjectsList.tsx rename to src/renderer/pages/ProjectsList/ProjectsListPage.tsx index 60d9277d..c3e03dbe 100644 --- a/src/renderer/pages/ProjectsList/ProjectsList.tsx +++ b/src/renderer/pages/ProjectsList/ProjectsListPage.tsx @@ -1,7 +1,7 @@ import React, { useCallback } from 'react'; import { useGetProjectsQuery } from '@/api'; import { useQueryParamState, useColumnOrderHandler } from '@/hooks'; -import { debounce } from 'lodash'; +import {debounce, DebouncedFunc} from 'lodash'; import { OrganizationSelector, IndeterminateProgressOverlay, @@ -11,7 +11,7 @@ import { } from '@/components'; import {FormattedMessage} from "react-intl"; -const ProjectsList: React.FC = () => { +const ProjectsListPage: React.FC = () => { const [currentPage, setCurrentPage] = useQueryParamState('page', '1'); const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined); const [search, setSearch] = useQueryParamState('search', undefined); @@ -25,7 +25,7 @@ const ProjectsList: React.FC = () => { error: projectsError, } = useGetProjectsQuery({ page: Number(currentPage), orgUid, search, order }); - const handlePageChange = useCallback( + const handlePageChange: DebouncedFunc = useCallback( debounce((page) => setCurrentPage(page), 800), [setCurrentPage], ); @@ -37,7 +37,7 @@ const ProjectsList: React.FC = () => { [setOrgUid], ); - const handleSearchChange = useCallback( + const handleSearchChange: DebouncedFunc = useCallback( debounce((event: any) => { setSearch(event.target.value); }, 800), @@ -83,4 +83,4 @@ const ProjectsList: React.FC = () => { ); }; -export { ProjectsList }; +export { ProjectsListPage }; diff --git a/src/renderer/pages/ProjectsList/index.ts b/src/renderer/pages/ProjectsList/index.ts index 5353d519..85e99f26 100644 --- a/src/renderer/pages/ProjectsList/index.ts +++ b/src/renderer/pages/ProjectsList/index.ts @@ -1 +1 @@ -export * from './ProjectsList'; \ No newline at end of file +export * from './ProjectsListPage'; \ No newline at end of file diff --git a/src/renderer/pages/UnitsList/UnitsList.tsx b/src/renderer/pages/UnitsList/UnitsListPage.tsx similarity index 90% rename from src/renderer/pages/UnitsList/UnitsList.tsx rename to src/renderer/pages/UnitsList/UnitsListPage.tsx index e47cfb18..431e013c 100644 --- a/src/renderer/pages/UnitsList/UnitsList.tsx +++ b/src/renderer/pages/UnitsList/UnitsListPage.tsx @@ -1,7 +1,7 @@ import React, { useCallback } from 'react'; import { useGetUnitsQuery } from '@/api'; import { useQueryParamState, useColumnOrderHandler } from '@/hooks'; -import { debounce } from 'lodash'; +import {debounce, DebouncedFunc} from 'lodash'; import { OrganizationSelector, IndeterminateProgressOverlay, @@ -11,7 +11,7 @@ import { } from '@/components'; import {FormattedMessage} from "react-intl"; -const UnitsList: React.FC = () => { +const UnitsListPage: React.FC = () => { const [currentPage, setCurrentPage] = useQueryParamState('page', '1'); const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined); const [search, setSearch] = useQueryParamState('search', undefined); @@ -25,7 +25,7 @@ const UnitsList: React.FC = () => { error: unitsError, } = useGetUnitsQuery({ page: Number(currentPage), orgUid, search, order }); - const handlePageChange = useCallback( + const handlePageChange: DebouncedFunc = useCallback( debounce((page) => setCurrentPage(page), 800), [setCurrentPage], ); @@ -37,7 +37,7 @@ const UnitsList: React.FC = () => { [setOrgUid], ); - const handleSearchChange = useCallback( + const handleSearchChange: DebouncedFunc = useCallback( debounce((event: any) => { setSearch(event.target.value); }, 800), @@ -82,4 +82,4 @@ const UnitsList: React.FC = () => { ); }; -export { UnitsList }; +export { UnitsListPage }; diff --git a/src/renderer/pages/UnitsList/index.ts b/src/renderer/pages/UnitsList/index.ts index ca0a28cb..3d284f52 100644 --- a/src/renderer/pages/UnitsList/index.ts +++ b/src/renderer/pages/UnitsList/index.ts @@ -1 +1 @@ -export * from './UnitsList'; \ No newline at end of file +export * from './UnitsListPage'; \ No newline at end of file diff --git a/src/renderer/pages/index.ts b/src/renderer/pages/index.ts index 64381782..69dfec76 100644 --- a/src/renderer/pages/index.ts +++ b/src/renderer/pages/index.ts @@ -1,3 +1,4 @@ export * from './Error'; export * from './ProjectsList'; export * from './UnitsList'; +export * from './Audit'; diff --git a/src/renderer/routes/AppNavigator.tsx b/src/renderer/routes/AppNavigator.tsx index 26e2e8ba..c4c3a2fb 100644 --- a/src/renderer/routes/AppNavigator.tsx +++ b/src/renderer/routes/AppNavigator.tsx @@ -26,11 +26,15 @@ const AppNavigator: React.FC = () => { /> } + element={} /> } + element={} + /> + } /> Date: Tue, 2 Apr 2024 14:45:38 -0400 Subject: [PATCH 2/2] feat: added format seconds to date time string --- .../components/blocks/tables/AuditsTable.tsx | 16 ++-------------- src/renderer/utils/transforms.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 14 deletions(-) create mode 100644 src/renderer/utils/transforms.ts diff --git a/src/renderer/components/blocks/tables/AuditsTable.tsx b/src/renderer/components/blocks/tables/AuditsTable.tsx index bbb62f13..51553e65 100644 --- a/src/renderer/components/blocks/tables/AuditsTable.tsx +++ b/src/renderer/components/blocks/tables/AuditsTable.tsx @@ -2,6 +2,7 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from 'react-intl'; import { DebouncedFunc } from 'lodash'; import { DataTable, PageCounter, Pagination } from '@/components'; +import {formatToDataTimeFromSeconds} from "@/utils/transforms"; interface TableProps { data: any[]; @@ -36,20 +37,7 @@ const AuditsTable: React.FC = ({ title: , key: 'onchainConfirmationTimeStamp', render: (row: any) => { - return ( - <> - { - new Intl.DateTimeFormat('en-US', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - }).format(new Date(row.onchainConfirmationTimeStamp * 1000)) - } - - ); + return <>{formatToDataTimeFromSeconds(row.onchainConfirmationTimeStamp)}; } }, { diff --git a/src/renderer/utils/transforms.ts b/src/renderer/utils/transforms.ts new file mode 100644 index 00000000..264950ad --- /dev/null +++ b/src/renderer/utils/transforms.ts @@ -0,0 +1,10 @@ +export const formatToDataTimeFromSeconds = (seconds: number): string => { + return new Intl.DateTimeFormat('en-US', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }).format(new Date(seconds * 1000)) +} \ No newline at end of file