Skip to content

Commit 77fa647

Browse files
committed
Add pagination
1 parent 03eadb6 commit 77fa647

File tree

9 files changed

+183
-43
lines changed

9 files changed

+183
-43
lines changed

site/src/AppRouter.tsx

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { RequirePermission } from "components/RequirePermission/RequirePermissio
44
import { SetupPage } from "pages/SetupPage/SetupPage"
55
import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateSettingsPage"
66
import { FC, lazy, Suspense, useContext } from "react"
7-
import { Navigate, Route, Routes } from "react-router-dom"
7+
import { Route, Routes } from "react-router-dom"
88
import { selectPermissions } from "xServices/auth/authSelectors"
99
import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors"
1010
import { XServiceContext } from "xServices/StateContext"
@@ -139,19 +139,15 @@ export const AppRouter: FC = () => {
139139
<Route
140140
index
141141
element={
142-
process.env.NODE_ENV === "production" ? (
143-
<Navigate to="/workspaces" />
144-
) : (
145-
<AuthAndFrame>
146-
<RequirePermission
147-
isFeatureVisible={
148-
featureVisibility[FeatureNames.AuditLog] && !!permissions?.viewAuditLog
149-
}
150-
>
151-
<AuditPage />
152-
</RequirePermission>
153-
</AuthAndFrame>
154-
)
142+
<AuthAndFrame>
143+
<RequirePermission
144+
isFeatureVisible={
145+
featureVisibility[FeatureNames.AuditLog] && !!permissions?.viewAuditLog
146+
}
147+
>
148+
<AuditPage />
149+
</RequirePermission>
150+
</AuthAndFrame>
155151
}
156152
></Route>
157153
</Route>

site/src/api/api.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,17 @@ export const getEntitlements = async (): Promise<TypesGen.Entitlements> => {
384384
return response.data
385385
}
386386

387-
export const getAuditLogs = async (): Promise<TypesGen.AuditLog[]> => {
388-
const response = await axios.get("/api/v2/audit")
387+
interface GetAuditLogsOptions {
388+
limit: number
389+
offset: number
390+
}
391+
392+
export const getAuditLogs = async (options: GetAuditLogsOptions): Promise<TypesGen.AuditLog[]> => {
393+
const response = await axios.get(`/api/v2/audit?limit=${options}&offset=${options.offset}`)
394+
return response.data
395+
}
396+
397+
export const getAuditLogsCount = async (): Promise<number> => {
398+
const response = await axios.get(`/api/v2/audit/count`)
389399
return response.data
390400
}

site/src/pages/AuditPage/AuditPage.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,31 @@ import { auditMachine } from "xServices/audit/auditXService"
44
import { AuditPageView } from "./AuditPageView"
55

66
const AuditPage: FC = () => {
7-
const [auditState] = useMachine(auditMachine)
8-
const { auditLogs } = auditState.context
7+
const [auditState, auditSend] = useMachine(auditMachine, {
8+
context: {
9+
page: 1,
10+
limit: 25,
11+
},
12+
})
13+
const { auditLogs, count, page, limit } = auditState.context
914

10-
return <AuditPageView auditLogs={auditLogs} />
15+
return (
16+
<AuditPageView
17+
auditLogs={auditLogs}
18+
count={count}
19+
page={page}
20+
limit={limit}
21+
onNext={() => {
22+
auditSend("NEXT")
23+
}}
24+
onPrevious={() => {
25+
auditSend("PREVIOUS")
26+
}}
27+
onGoToPage={(page) => {
28+
auditSend("GO_TO_PAGE", { page })
29+
}}
30+
/>
31+
)
1132
}
1233

1334
export default AuditPage
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
import { ComponentMeta, Story } from "@storybook/react"
22
import { MockAuditLog, MockAuditLog2 } from "testHelpers/entities"
3-
import { AuditPageView } from "./AuditPageView"
3+
import { AuditPageView, AuditPageViewProps } from "./AuditPageView"
44

55
export default {
66
title: "pages/AuditPageView",
77
component: AuditPageView,
88
} as ComponentMeta<typeof AuditPageView>
99

10-
const Template: Story = (args) => <AuditPageView {...args} />
10+
const Template: Story<AuditPageViewProps> = (args) => <AuditPageView {...args} />
1111

1212
export const AuditPage = Template.bind({})
1313
AuditPage.args = {
1414
auditLogs: [MockAuditLog, MockAuditLog2],
15+
count: 1000,
16+
page: 1,
17+
limit: 25,
1518
}
1619

1720
export const AuditPageSmallViewport = Template.bind({})
21+
AuditPageSmallViewport.args = {
22+
auditLogs: [MockAuditLog, MockAuditLog2],
23+
count: 1000,
24+
page: 1,
25+
limit: 25,
26+
}
1827
AuditPageSmallViewport.parameters = {
1928
chromatic: { viewports: [600] },
2029
}

site/src/pages/AuditPage/AuditPageView.tsx

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { AuditLogRow } from "components/AuditLogRow/AuditLogRow"
99
import { CodeExample } from "components/CodeExample/CodeExample"
1010
import { Margins } from "components/Margins/Margins"
1111
import { PageHeader, PageHeaderSubtitle, PageHeaderTitle } from "components/PageHeader/PageHeader"
12+
import { PaginationWidget } from "components/PaginationWidget/PaginationWidget"
1213
import { Stack } from "components/Stack/Stack"
1314
import { TableLoader } from "components/TableLoader/TableLoader"
1415
import { AuditHelpTooltip } from "components/Tooltips"
@@ -20,7 +21,27 @@ export const Language = {
2021
tooltipTitle: "Copy to clipboard and try the Coder CLI",
2122
}
2223

23-
export const AuditPageView: FC<{ auditLogs?: AuditLog[] }> = ({ auditLogs }) => {
24+
export interface AuditPageViewProps {
25+
auditLogs?: AuditLog[]
26+
count?: number
27+
page: number
28+
limit: number
29+
onNext: () => void
30+
onPrevious: () => void
31+
onGoToPage: (page: number) => void
32+
}
33+
34+
export const AuditPageView: FC<AuditPageViewProps> = ({
35+
auditLogs,
36+
count,
37+
page,
38+
limit,
39+
onNext,
40+
onPrevious,
41+
onGoToPage,
42+
}) => {
43+
const isReady = auditLogs && count
44+
2445
return (
2546
<Margins>
2647
<PageHeader
@@ -45,14 +66,27 @@ export const AuditPageView: FC<{ auditLogs?: AuditLog[] }> = ({ auditLogs }) =>
4566
</TableRow>
4667
</TableHead>
4768
<TableBody>
48-
{auditLogs ? (
69+
{isReady ? (
4970
auditLogs.map((auditLog) => <AuditLogRow auditLog={auditLog} key={auditLog.id} />)
5071
) : (
5172
<TableLoader />
5273
)}
5374
</TableBody>
5475
</Table>
5576
</TableContainer>
77+
78+
{isReady && count > limit && (
79+
<PaginationWidget
80+
prevLabel=""
81+
nextLabel=""
82+
onPrevClick={onPrevious}
83+
onNextClick={onNext}
84+
onPageClick={onGoToPage}
85+
numRecords={count}
86+
activePage={page}
87+
numRecordsPerPage={limit}
88+
/>
89+
)}
5690
</Margins>
5791
)
5892
}

site/src/testHelpers/entities.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -704,14 +704,14 @@ export const MockAuditLog: TypesGen.AuditLog = {
704704
user_agent: "browser",
705705
resource_type: "workspace",
706706
resource_id: "ef8d1cf4-82de-4fd9-8980-047dad6d06b5",
707-
resource_target: "Bruno's Org",
707+
resource_target: "bruno-dev",
708+
resource_icon: "",
708709
action: "create",
709710
diff: {},
710711
status_code: 200,
711-
additional_fields: {},
712-
description: "Colin Adler updated the organization Bruno's Org",
712+
additional_fields: "",
713+
description: "Colin Adler updated the workspace bruno-dev",
713714
user: MockUser,
714-
resource: MockWorkspace,
715715
}
716716

717717
export const MockAuditLog2: TypesGen.AuditLog = {
@@ -722,14 +722,17 @@ export const MockAuditLog2: TypesGen.AuditLog = {
722722
workspace_name: {
723723
old: "old-workspace-name",
724724
new: MockWorkspace.name,
725+
secret: false,
725726
},
726727
workspace_auto_off: {
727728
old: true,
728729
new: false,
730+
secret: false,
729731
},
730732
template_version_id: {
731733
old: "fbd2116a-8961-4954-87ae-e4575bd29ce0",
732734
new: "53bded77-7b9d-4e82-8771-991a34d759f9",
735+
secret: false,
733736
},
734737
},
735738
}

site/src/theme/overrides.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,16 @@ export const getOverrides = ({ palette, breakpoints }: Theme): Overrides => {
4141
boxShadow: "none",
4242
color: palette.text.primary,
4343
backgroundColor: colors.gray[17],
44+
4445
"&:hover": {
4546
boxShadow: "none",
4647
backgroundColor: "#000000",
4748
},
49+
50+
"&.Mui-disabled": {
51+
backgroundColor: palette.background.paper,
52+
color: palette.secondary.main,
53+
},
4854
},
4955
sizeSmall: {
5056
padding: `0 12px`,

site/src/theme/palettes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const darkPalette: PaletteOptions = {
1313
dark: colors.blue[9],
1414
},
1515
secondary: {
16-
main: colors.green[11],
16+
main: colors.gray[11],
1717
contrastText: colors.gray[3],
1818
dark: colors.indigo[7],
1919
},
Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getAuditLogs } from "api/api"
1+
import { getAuditLogs, getAuditLogsCount } from "api/api"
22
import { getErrorMessage } from "api/errors"
33
import { displayError } from "components/GlobalSnackbar/utils"
44
import { assign, createMachine } from "xstate"
@@ -9,31 +9,70 @@ export const auditMachine = createMachine(
99
{
1010
id: "auditMachine",
1111
schema: {
12-
context: {} as { auditLogs?: AuditLogs },
12+
context: {} as { auditLogs?: AuditLogs; count?: number; page: number; limit: number },
1313
services: {} as {
1414
loadAuditLogs: {
1515
data: AuditLogs
1616
}
17+
loadAuditLogsCount: {
18+
data: number
19+
}
1720
},
21+
events: {} as
22+
| {
23+
type: "NEXT"
24+
}
25+
| {
26+
type: "PREVIOUS"
27+
}
28+
| {
29+
type: "GO_TO_PAGE"
30+
page: number
31+
},
1832
},
1933
tsTypes: {} as import("./auditXService.typegen").Typegen0,
20-
initial: "loadingLogs",
34+
initial: "loading",
2135
states: {
22-
loadingLogs: {
23-
invoke: {
24-
src: "loadAuditLogs",
25-
onDone: {
26-
target: "success",
27-
actions: ["assignAuditLogs"],
36+
loading: {
37+
invoke: [
38+
{
39+
src: "loadAuditLogs",
40+
onDone: {
41+
actions: ["assignAuditLogs"],
42+
},
43+
onError: {
44+
target: "error",
45+
actions: ["displayLoadAuditLogsError"],
46+
},
2847
},
29-
onError: {
30-
target: "error",
31-
actions: ["displayLoadAuditLogsError"],
48+
{
49+
src: "loadAuditLogsCount",
50+
onDone: {
51+
actions: ["assignCount"],
52+
},
53+
onError: {
54+
target: "error",
55+
actions: ["displayLoadAuditLogsCountError"],
56+
},
3257
},
33-
},
58+
],
59+
onDone: "success",
3460
},
3561
success: {
36-
type: "final",
62+
on: {
63+
NEXT: {
64+
actions: ["assignNextPage"],
65+
target: "loading",
66+
},
67+
PREVIOUS: {
68+
actions: ["assignPreviousPage"],
69+
target: "loading",
70+
},
71+
GO_TO_PAGE: {
72+
actions: ["assignPage"],
73+
target: "loading",
74+
},
75+
},
3776
},
3877
error: {
3978
type: "final",
@@ -45,13 +84,35 @@ export const auditMachine = createMachine(
4584
assignAuditLogs: assign({
4685
auditLogs: (_, event) => event.data,
4786
}),
87+
assignCount: assign({
88+
count: (_, event) => event.data,
89+
}),
90+
assignNextPage: assign({
91+
page: ({ page }) => page + 1,
92+
}),
93+
assignPreviousPage: assign({
94+
page: ({ page }) => page - 1,
95+
}),
96+
assignPage: assign({
97+
page: ({ page }) => page,
98+
}),
4899
displayLoadAuditLogsError: (_, event) => {
49100
const message = getErrorMessage(event.data, "Error on loading audit logs.")
50101
displayError(message)
51102
},
103+
displayLoadAuditLogsCountError: (_, event) => {
104+
const message = getErrorMessage(event.data, "Error on loading audit logs count.")
105+
displayError(message)
106+
},
52107
},
53108
services: {
54-
loadAuditLogs: () => getAuditLogs(),
109+
loadAuditLogs: ({ page, limit }, _) =>
110+
getAuditLogs({
111+
// The page in the API starts at 0
112+
offset: (page - 1) * limit,
113+
limit,
114+
}),
115+
loadAuditLogsCount: () => getAuditLogsCount(),
55116
},
56117
},
57118
)

0 commit comments

Comments
 (0)