Skip to content

Commit a3b3183

Browse files
committed
feat(web-ui): add organization journal page
1 parent dc8d964 commit a3b3183

6 files changed

+212
-0
lines changed

services/web-ui/src/Router.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { CreateOrganizationPage } from "src/pages/CreateOrganizationPage";
1212
import { CurrentOrganizationPage } from "src/pages/CurrentOrganizationPage";
1313
import { CurrentProfilePage } from "src/pages/CurrentProfilePage";
1414
import { NotFoundPage } from "src/pages/NotFoundPage";
15+
import { OrganizationJournalPage } from "src/pages/OrganizationJournalPage";
1516
import { OrganizationListPage } from "src/pages/OrganizationListPage";
1617
import { ProfileGuard } from "src/pages/ProfileGuard";
1718
import { ProfileJournalPage } from "src/pages/ProfileJournalPage";
@@ -57,6 +58,10 @@ const router = createBrowserRouter([
5758
path: "/group/create",
5859
element: <CreateOrganizationPage />,
5960
},
61+
{
62+
path: "/group/journal",
63+
element: <OrganizationJournalPage />,
64+
},
6065
{
6166
path: "/group/list",
6267
element: <OrganizationListPage />,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { ReactElement } from "react";
2+
3+
import { JournalEntryDetails } from "src/api";
4+
import { Table } from "src/components/ui";
5+
import { Column } from "src/components/ui/Table";
6+
import { format } from "src/utils/string";
7+
8+
const columns: Column<JournalEntryDetails>[] = [
9+
{
10+
field: "commandName",
11+
headerName: "Action",
12+
valueGetter: (params) => format(params.value || ""),
13+
width: 400,
14+
},
15+
{
16+
field: "createdAt",
17+
headerName: "Date",
18+
valueGetter: (params) => params.value.toLocaleString(),
19+
width: 400,
20+
},
21+
];
22+
23+
interface OrganizationJournalPageComponentProps {
24+
data: JournalEntryDetails[];
25+
loading?: boolean;
26+
}
27+
28+
export function OrganizationJournalPageComponent({
29+
data,
30+
loading,
31+
}: OrganizationJournalPageComponentProps): ReactElement {
32+
return (
33+
<Table
34+
columns={columns}
35+
height="60vh"
36+
initialState={{
37+
sorting: {
38+
sortModel: [{ field: "created", sort: "desc" }],
39+
},
40+
}}
41+
loading={loading}
42+
rows={data}
43+
/>
44+
);
45+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React, { useState } from "react";
2+
3+
import { Box, Typography } from "@mui/material";
4+
5+
import { journalApi } from "src/apis";
6+
import { ErrorMessage } from "src/components/ui/ErrorMessage";
7+
import { CacheKeysConstants, useQuery } from "src/core/query";
8+
import { getDateDaysAgo } from "src/utils/time";
9+
10+
import { OrganizationJournalPageComponent } from "./OrganizationJournalPage.component";
11+
import { OrganizationJournalPageNav } from "./OrganizationJournalPage.nav";
12+
13+
export function OrganizationJournalPageContainer(): React.ReactElement {
14+
const [dateRange, setDateRange] = useState({
15+
to: new Date(),
16+
from: getDateDaysAgo(2),
17+
});
18+
19+
const { error, data, isLoading } = useQuery(
20+
[CacheKeysConstants.Journal, dateRange.from, dateRange.to],
21+
() => journalApi.getCurrentOrganizationJournal(dateRange)
22+
);
23+
24+
if (error) {
25+
return (
26+
<OrganizationJournalPageNav
27+
onDateChange={setDateRange}
28+
values={dateRange}
29+
>
30+
<ErrorMessage error={error} />
31+
</OrganizationJournalPageNav>
32+
);
33+
}
34+
35+
if (isLoading) {
36+
return (
37+
<OrganizationJournalPageNav
38+
onDateChange={setDateRange}
39+
values={dateRange}
40+
>
41+
<Box sx={{ marginTop: 2 }}>
42+
<OrganizationJournalPageComponent data={[]} loading />
43+
</Box>
44+
</OrganizationJournalPageNav>
45+
);
46+
}
47+
48+
if (!data) {
49+
return (
50+
<OrganizationJournalPageNav
51+
onDateChange={setDateRange}
52+
values={dateRange}
53+
>
54+
<Typography>No entries found in journal</Typography>
55+
</OrganizationJournalPageNav>
56+
);
57+
}
58+
59+
return (
60+
<OrganizationJournalPageNav onDateChange={setDateRange} values={dateRange}>
61+
<Box sx={{ marginTop: 2 }}>
62+
<OrganizationJournalPageComponent data={data.entries} />
63+
</Box>
64+
</OrganizationJournalPageNav>
65+
);
66+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { ReactElement, ReactNode } from "react";
2+
3+
import { Grid, Typography } from "@mui/material";
4+
5+
import { NavLayout } from "src/components/layout";
6+
import { DatePicker } from "src/components/ui/DatePicker";
7+
import { useTranslation } from "src/core/i18n";
8+
9+
export interface DateRange {
10+
from: Date;
11+
to: Date;
12+
}
13+
14+
interface OrganizationJournalPageNavProps {
15+
children: ReactNode;
16+
onDateChange: (value: DateRange) => void;
17+
values: DateRange;
18+
}
19+
20+
export function OrganizationJournalPageNav({
21+
children,
22+
onDateChange,
23+
values,
24+
}: OrganizationJournalPageNavProps): ReactElement {
25+
const { t } = useTranslation("journal");
26+
27+
return (
28+
<NavLayout header={t("header")} linkText={t("links.back")} to="/profile">
29+
<Typography color="textSecondary" sx={{ paddingBottom: 3 }}>
30+
{t("description")}
31+
</Typography>
32+
33+
<Grid container spacing={2}>
34+
<Grid item xs={6}>
35+
<DatePicker
36+
fullWidth
37+
label="From"
38+
onChange={(from) => onDateChange({ ...values, from })}
39+
value={values.from}
40+
/>
41+
</Grid>
42+
43+
<Grid item xs={6}>
44+
<DatePicker
45+
fullWidth
46+
label="To"
47+
onChange={(to) => onDateChange({ ...values, to })}
48+
value={values.to}
49+
/>
50+
</Grid>
51+
</Grid>
52+
53+
{children}
54+
</NavLayout>
55+
);
56+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
3+
import { JournalEntryDetails } from "src/api";
4+
5+
import { ProfileJournalPageComponent } from "./ProfileJournalPage.component";
6+
7+
const JOURNAL_ENTRIES: JournalEntryDetails[] = [
8+
{
9+
commandName: "Did the thing",
10+
createdAt: new Date("2020"),
11+
id: "1",
12+
payload: {},
13+
profile: {
14+
id: 1,
15+
},
16+
},
17+
{
18+
commandName: "Did the thing again",
19+
createdAt: new Date("2020"),
20+
id: "2",
21+
payload: {},
22+
profile: {
23+
id: 1,
24+
},
25+
},
26+
];
27+
28+
export default {
29+
title: "Pages/Journal/View",
30+
component: ProfileJournalPageComponent,
31+
} as Meta;
32+
33+
type Story = StoryObj<typeof ProfileJournalPageComponent>;
34+
35+
export const Default: Story = {
36+
args: {
37+
data: JOURNAL_ENTRIES,
38+
},
39+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { OrganizationJournalPageContainer as OrganizationJournalPage } from "./OrganizationJournalPage.container";

0 commit comments

Comments
 (0)