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-router-dom": "^6.4.2",
"react-use": "^17.4.0", "react-use": "^17.4.0",
"secure-random-password": "^0.2.3", "secure-random-password": "^0.2.3",
"sort-array": "^4.1.5",
"ua-parser-js": "^1.0.2", "ua-parser-js": "^1.0.2",
"use-system-theme": "^0.1.1", "use-system-theme": "^0.1.1",
"yup": "^0.32.11" "yup": "^0.32.11"
@ -46,6 +47,7 @@
"@types/react-router": "^5.1.19", "@types/react-router": "^5.1.19",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"@types/secure-random-password": "^0.2.1", "@types/secure-random-password": "^0.2.1",
"@types/sort-array": "^4.1.0",
"@types/ua-parser-js": "^0.7.36", "@types/ua-parser-js": "^0.7.36",
"@types/yup": "^0.32.0", "@types/yup": "^0.32.0",
"@typescript-eslint/eslint-plugin": "^5.40.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 {useAsync} from "react-use"
import camelcaseKeys from "camelcase-keys" import camelcaseKeys from "camelcase-keys"
import {DecryptedReportContent} from "~/server-types" import {DecryptedReportContent, Report} from "~/server-types"
import AuthContext from "~/AuthContext/AuthContext" import AuthContext from "~/AuthContext/AuthContext"
import parseDecryptedReport from "~/apis/helpers/parse-decrypted-report" 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 encryptedContent: string
children: (report: DecryptedReportContent) => ReactElement children: (report: DecryptedReportContent) => ReactElement
} }
interface DecryptReportPropsReports {
reports: Report[]
children: (reports: DecryptedReportContent[]) => ReactElement
}
export type DecryptReportProps = DecryptReportPropsBase &
(DecryptReportPropsEncryptedContent | DecryptReportPropsReports)
export default function DecryptReport({ export default function DecryptReport({
encryptedContent, encryptedContent,
reports,
children: render, children: render,
}: DecryptReportProps): ReactElement { }: DecryptReportProps): ReactElement {
const {_decryptUsingPrivateKey} = useContext(AuthContext) const {_decryptUsingPrivateKey} = useContext(AuthContext)
const {value} = useAsync(async () => { const {value} = useAsync(async () => {
const message = await _decryptUsingPrivateKey(encryptedContent) const decrypt = async (
const content = camelcaseKeys(JSON.parse(message), {deep: true}) content: string,
): Promise<DecryptedReportContent> =>
parseDecryptedReport(
camelcaseKeys(
JSON.parse(await _decryptUsingPrivateKey(content)),
{deep: true},
),
)
return parseDecryptedReport(content) if (encryptedContent) {
}, [encryptedContent]) return decrypt(encryptedContent)
} else {
return await Promise.all(
reports!.map(report => decrypt(report.encryptedContent)),
)
}
}, [encryptedContent, reports])
if (!value) { if (!value) {
return <></> return <></>
} }
console.log(value)
return render(value) return render(value)
} }

View File

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

View File

@ -34,10 +34,10 @@ const SECTION_TEXT_MAP: Record<NavigationSection, string> = {
} }
const PATH_SECTION_MAP: Record<string, NavigationSection> = { const PATH_SECTION_MAP: Record<string, NavigationSection> = {
"/": NavigationSection.Overview, "": NavigationSection.Overview,
"/aliases": NavigationSection.Aliases, aliases: NavigationSection.Aliases,
"/reports": NavigationSection.Reports, reports: NavigationSection.Reports,
"/settings": NavigationSection.Settings, settings: NavigationSection.Settings,
} }
export default function NavigationButton({ export default function NavigationButton({
@ -45,7 +45,7 @@ export default function NavigationButton({
}: NavigationButtonProps): ReactElement { }: NavigationButtonProps): ReactElement {
const location = useLocation() 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 Icon = SECTION_ICON_MAP[section]
const text = SECTION_TEXT_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>
<Grid item xs={12} sm={8} md={10}> <Grid item xs={12} sm={8} md={10}>
<Paper> <Paper>
<Box padding={4} maxHeight="60vh" overflow="scroll"> <Box padding={4}>
<Outlet /> <Outlet />
</Box> </Box>
</Paper> </Paper>

View File

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

View File

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