improved ReportsList.tsx

This commit is contained in:
Myzel394 2022-10-23 11:16:19 +02:00
parent c7381ed5c6
commit 044c98f348
8 changed files with 236 additions and 64 deletions

View File

@ -31,6 +31,7 @@
"react-router-dom": "^6.4.2",
"react-use": "^17.4.0",
"secure-random-password": "^0.2.3",
"sort-array": "^4.1.5",
"ua-parser-js": "^1.0.2",
"use-system-theme": "^0.1.1",
"yup": "^0.32.11"
@ -46,6 +47,7 @@
"@types/react-router": "^5.1.19",
"@types/react-router-dom": "^5.3.3",
"@types/secure-random-password": "^0.2.1",
"@types/sort-array": "^4.1.0",
"@types/ua-parser-js": "^0.7.36",
"@types/yup": "^0.32.0",
"@typescript-eslint/eslint-plugin": "^5.40.0",

View File

@ -2,31 +2,63 @@ import {ReactElement, useContext} from "react"
import {useAsync} from "react-use"
import camelcaseKeys from "camelcase-keys"
import {DecryptedReportContent} from "~/server-types"
import {DecryptedReportContent, Report} from "~/server-types"
import AuthContext from "~/AuthContext/AuthContext"
import parseDecryptedReport from "~/apis/helpers/parse-decrypted-report"
export interface DecryptReportProps {
interface DecryptReportPropsBase {
encryptedContent?: string
reports?: Report[]
children: (
report: DecryptedReportContent | DecryptedReportContent[],
) => ReactElement
}
interface DecryptReportPropsEncryptedContent {
encryptedContent: string
children: (report: DecryptedReportContent) => ReactElement
}
interface DecryptReportPropsReports {
reports: Report[]
children: (reports: DecryptedReportContent[]) => ReactElement
}
export type DecryptReportProps = DecryptReportPropsBase &
(DecryptReportPropsEncryptedContent | DecryptReportPropsReports)
export default function DecryptReport({
encryptedContent,
reports,
children: render,
}: DecryptReportProps): ReactElement {
const {_decryptUsingPrivateKey} = useContext(AuthContext)
const {value} = useAsync(async () => {
const message = await _decryptUsingPrivateKey(encryptedContent)
const content = camelcaseKeys(JSON.parse(message), {deep: true})
const decrypt = async (
content: string,
): Promise<DecryptedReportContent> =>
parseDecryptedReport(
camelcaseKeys(
JSON.parse(await _decryptUsingPrivateKey(content)),
{deep: true},
),
)
return parseDecryptedReport(content)
}, [encryptedContent])
if (encryptedContent) {
return decrypt(encryptedContent)
} else {
return await Promise.all(
reports!.map(report => decrypt(report.encryptedContent)),
)
}
}, [encryptedContent, reports])
if (!value) {
return <></>
}
console.log(value)
return render(value)
}

View File

@ -12,8 +12,6 @@ export default function WithEncryptionRequired(
return (props: any): ReactElement => {
const user = useUser()
console.log("withencryption required", user)
if (!user.encryptedPassword) {
return (
<Grid container spacing={4}>

View File

@ -34,10 +34,10 @@ const SECTION_TEXT_MAP: Record<NavigationSection, string> = {
}
const PATH_SECTION_MAP: Record<string, NavigationSection> = {
"/": NavigationSection.Overview,
"/aliases": NavigationSection.Aliases,
"/reports": NavigationSection.Reports,
"/settings": NavigationSection.Settings,
"": NavigationSection.Overview,
aliases: NavigationSection.Aliases,
reports: NavigationSection.Reports,
settings: NavigationSection.Settings,
}
export default function NavigationButton({
@ -45,7 +45,7 @@ export default function NavigationButton({
}: NavigationButtonProps): ReactElement {
const location = useLocation()
const currentSection = PATH_SECTION_MAP[location.pathname]
const currentSection = PATH_SECTION_MAP[location.pathname.split("/")[1]]
const Icon = SECTION_ICON_MAP[section]
const text = SECTION_TEXT_MAP[section]

View File

@ -0,0 +1,123 @@
import {ReactElement, useState} from "react"
import {MdList} from "react-icons/md"
import {FaMask} from "react-icons/fa"
import groupArray from "group-array"
import sortArray from "sort-array"
import {
Grid,
InputAdornment,
MenuItem,
TextField,
Typography,
} from "@mui/material"
import {DecryptedReportContent} from "~/server-types"
import ReportInformationItem from "./ReportInformationItem"
export interface ReportsListProps {
reports: DecryptedReportContent[]
}
enum SortingView {
List = "List",
GroupByAlias = "GroupByAlias",
}
const SORTING_VIEW_NAME_MAP: Record<SortingView, string> = {
[SortingView.List]: "List reports by their date",
[SortingView.GroupByAlias]: "Group reports by their aliases",
}
const SORTING_VIEW_ICON_MAP: Record<SortingView, ReactElement> = {
[SortingView.List]: <MdList />,
[SortingView.GroupByAlias]: <FaMask />,
}
export default function ReportsList({reports}: ReportsListProps): ReactElement {
const [sortingView, setSortingView] = useState<SortingView>(
SortingView.List,
)
return (
<Grid direction="column" container spacing={4}>
<Grid item>
<Typography variant="h6" component="h2">
Reports
</Typography>
</Grid>
<Grid item>
<TextField
value={sortingView}
onChange={event =>
setSortingView(event.target.value as SortingView)
}
label="Sorting"
id="sorting"
InputProps={{
startAdornment: (
<InputAdornment position="start">
{SORTING_VIEW_ICON_MAP[sortingView]}
</InputAdornment>
),
}}
select
>
{Object.keys(SORTING_VIEW_NAME_MAP).map(name => (
<MenuItem key={name} value={name}>
{SORTING_VIEW_NAME_MAP[name as SortingView]}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item>
{(() => {
switch (sortingView) {
case SortingView.List:
return sortArray(
reports as DecryptedReportContent[],
{
by: "messageDetails.meta.createdAt",
order: "desc",
},
).map(report => (
<ReportInformationItem
report={report}
key={report.id}
/>
))
case SortingView.GroupByAlias:
return Object.entries(
groupArray(
reports as DecryptedReportContent[],
"messageDetails.meta.to",
),
).map(
([alias, reports]: [
string,
DecryptedReportContent[],
]) => (
<>
<Typography
variant="caption"
component="h2"
>
{alias}
</Typography>
{reports.map(report => (
<ReportInformationItem
report={report}
key={report.id}
/>
))}
</>
),
)
}
})()}
</Grid>
</Grid>
)
}

View File

@ -56,7 +56,7 @@ export default function AuthenticatedRoute(): ReactElement {
</Grid>
<Grid item xs={12} sm={8} md={10}>
<Paper>
<Box padding={4} maxHeight="60vh" overflow="scroll">
<Box padding={4}>
<Outlet />
</Box>
</Paper>

View File

@ -27,48 +27,71 @@ export default function ReportDetailRoute(): ReactElement {
>
{report => (
<Grid container spacing={4}>
<Grid item>
<Grid item xs={12}>
<Typography variant="h4" component="h1">
Email Report
</Typography>
</Grid>
<Grid item>
<Grid item xs={12}>
<Typography variant="h6" component="h2">
Email information
</Typography>
<Box component="dl">
<Typography
variant="overline"
component="dt"
>
From
</Typography>
<Typography variant="body1" component="dd">
{report.messageDetails.meta.from}
</Typography>
</Box>
<Box component="dl">
<Typography
variant="overline"
component="dt"
>
To
</Typography>
<Typography variant="body1" component="dd">
{report.messageDetails.meta.to}
</Typography>
</Box>
<Box component="dl">
<Typography
variant="overline"
component="dt"
>
Subject
</Typography>
<Typography variant="body1" component="dd">
{report.messageDetails.content.subject}
</Typography>
</Box>
<Grid container columnSpacing={4}>
<Grid item xs={12} md={6} lg={4}>
<Box component="dl">
<Typography
variant="overline"
component="dt"
>
From
</Typography>
<Typography
variant="body1"
component="dd"
>
{
report.messageDetails.meta
.from
}
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={6} lg={4}>
<Box component="dl">
<Typography
variant="overline"
component="dt"
>
To
</Typography>
<Typography
variant="body1"
component="dd"
>
{report.messageDetails.meta.to}
</Typography>
</Box>
</Grid>
<Grid item xs={12} lg={4}>
<Box component="dl">
<Typography
variant="overline"
component="dt"
>
Subject
</Typography>
<Typography
variant="body1"
component="dd"
>
{
report.messageDetails
.content.subject
}
</Typography>
</Box>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="h6" component="h2">

View File

@ -4,12 +4,12 @@ import {AxiosError} from "axios"
import {useQuery} from "@tanstack/react-query"
import {List} from "@mui/material"
import {PaginationResult, Report} from "~/server-types"
import {DecryptedReportContent, PaginationResult, Report} from "~/server-types"
import {getReports} from "~/apis"
import {WithEncryptionRequired} from "~/hocs"
import {DecryptReport} from "~/components"
import QueryResult from "~/components/QueryResult"
import ReportInformationItem from "~/route-widgets/ReportsRoute/ReportInformationItem"
import ReportsList from "~/route-widgets/ReportsRoute/ReportsList"
function ReportsRoute(): ReactElement {
const query = useQuery<PaginationResult<Report>, AxiosError>(
@ -21,19 +21,13 @@ function ReportsRoute(): ReactElement {
<QueryResult<PaginationResult<Report>> query={query}>
{result => (
<List>
{result.items.map(report => (
<DecryptReport
key={report.id}
encryptedContent={report.encryptedContent}
>
{report => (
<ReportInformationItem
report={report}
key={report.id}
/>
)}
</DecryptReport>
))}
<DecryptReport reports={result.items}>
{reports => (
<ReportsList
reports={reports as DecryptedReportContent[]}
/>
)}
</DecryptReport>
</List>
)}
</QueryResult>