Skip to content

Commit b694655

Browse files
committed
feat: web monitor pdf
fix: location function call
1 parent e87f3ff commit b694655

48 files changed

Lines changed: 2055 additions & 56 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

clients/admin-ui/global.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ declare module globalThis {
55
let fidesDebugger: (...args: unknown[]) => void;
66
}
77

8+
declare module "*.ttf";
9+
810
interface Window {
911
// Cypress is available on window when running in Cypress tests
1012
Cypress?: any;

clients/admin-ui/jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ module.exports = {
5151
transformIgnorePatterns: [
5252
"/node_modules/(?!(react-hotkeys-hook|@ant-design/x-markdown)/)",
5353
"^.+\\.module\\.(css|sass|scss)$",
54+
"^.+.(ttf)$",
5455
],
5556
watchPathIgnorePatterns: ["node_modules"],
5657
};

clients/admin-ui/next.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ const nextConfig = {
2121
resolveAlias: {
2222
antd: "antd/lib",
2323
},
24+
rules: {
25+
"*.ttf": {
26+
type: "asset",
27+
},
28+
"*.woff2": {
29+
type: "asset",
30+
},
31+
},
2432
},
2533
webpack: (config) => {
2634
config.resolve.alias = {

clients/admin-ui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@json-render/core": "^0.18.0",
4646
"@json-render/react": "^0.18.0",
4747
"@monaco-editor/react": "^4.6.0",
48+
"@react-pdf/renderer": "^4.5.1",
4849
"@reduxjs/toolkit": "^2.6.0",
4950
"@tanstack/react-table": "^8.10.7",
5051
"@types/dagre": "^0.7.52",
@@ -78,6 +79,7 @@
7879
"react-hotkeys-hook": "^5.2.1",
7980
"react-phone-number-input": "^3.4.1",
8081
"react-redux": "^9.1.2",
82+
"recharts": "^3.7.0",
8183
"redux-persist": "^6.0.0",
8284
"valibot": "^1.2.0",
8385
"yup": "^1.4.0",

clients/admin-ui/src/features/common/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ export const getBrandIconUrl = (domain: string, size = 24) => {
140140
return `https://cdn.brandfetch.io/${domain}/icon/theme/light/fallback/404/h/${size}/w/${size}?c=1idbRjELpikqQ1PLiqb`;
141141
};
142142

143+
export const getBrandLogoUrl = (domain: string, size = 300) =>
144+
`https://cdn.brandfetch.io/domain/${domain}/type/logo.png/theme/dark/fallback/404/h/${size}/w/${size * 3}?c=1idbRjELpikqQ1PLiqb`;
145+
143146
export const getDomain = (urlOrDomain: string): string => {
144147
try {
145148
// Try to parse as URL first

clients/admin-ui/src/features/data-discovery-and-detection/action-center/action-center.slice.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ import {
2121
import { AggregateStatisticsResponse } from "~/types/api/models/AggregateStatisticsResponse";
2222
import { APIMonitorType } from "~/types/api/models/APIMonitorType";
2323
import { BaseStagedResourcesRequest } from "~/types/api/models/BaseStagedResourcesRequest";
24+
import { ClassificationBreakdownResponse } from "~/types/api/models/ClassificationBreakdownResponse";
2425
import { ConditionalTotalCursorPage_DatastoreStagedResourceTreeAPIResponse_ } from "~/types/api/models/ConditionalTotalCursorPage_DatastoreStagedResourceTreeAPIResponse_";
2526
import { DatastoreMonitorResourcesDynamicFilters } from "~/types/api/models/DatastoreMonitorResourcesDynamicFilters";
2627
import { DatastoreStagedResourceTreeAPIResponse } from "~/types/api/models/DatastoreStagedResourceTreeAPIResponse";
2728
import { ExecutionLogStatus } from "~/types/api/models/ExecutionLogStatus";
2829
import { MonitorActionResponse } from "~/types/api/models/MonitorActionResponse";
2930
import { MonitorTaskResponse } from "~/types/api/models/MonitorTaskResponse";
31+
import { VendorBreakdownResponse } from "~/types/api/models/VendorBreakdownResponse";
3032
import {
3133
PaginatedResponse,
3234
PaginationQueryParams,
@@ -545,7 +547,34 @@ const actionCenterApi = baseApi.injectEndpoints({
545547
},
546548
providesTags: ["Monitor Tasks"],
547549
}),
548-
550+
getVendors: build.query<
551+
VendorBreakdownResponse,
552+
{
553+
monitor_config_id?: string;
554+
}
555+
>({
556+
query: ({ ...params }) => {
557+
return {
558+
url: `/plus/discovery-monitor/report/vendors`,
559+
params,
560+
};
561+
},
562+
// providesTags: ["Monitor Statistics"],
563+
}),
564+
getClassifications: build.query<
565+
ClassificationBreakdownResponse,
566+
{
567+
monitor_config_id?: string;
568+
}
569+
>({
570+
query: ({ ...params }) => {
571+
return {
572+
url: `/plus/discovery-monitor/report/classifications`,
573+
params,
574+
};
575+
},
576+
// providesTags: ["Monitor Statistics"],
577+
}),
549578
getAggregateStatistics: build.query<
550579
AggregateStatisticsResponse,
551580
{
@@ -680,6 +709,7 @@ export const {
680709
useLazyGetMonitorTreeQuery,
681710
useLazyGetMonitorTreeAncestorsStatusesQuery,
682711
useGetMonitorConfigQuery,
712+
useLazyGetMonitorConfigQuery,
683713
useGetDatastoreFiltersQuery,
684714
useClassifyStagedResourcesMutation,
685715
useGetStagedResourceDetailsQuery,
@@ -689,5 +719,9 @@ export const {
689719
useGetAggregateStatisticsQuery,
690720
useGetConnectionQuery,
691721
useCalcAggregateStatisticsMutation,
722+
useGetVendorsQuery,
723+
useLazyGetVendorsQuery,
724+
useGetClassificationsQuery,
725+
useLazyGetClassificationsQuery,
692726
util: actionCenterUtil,
693727
} = actionCenterApi;
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { Page, StyleSheet, Text, View } from "@react-pdf/renderer";
2+
import { palette } from "fidesui/src/palette/palette";
3+
4+
import { PdfFooter } from "~/features/pdf/Footer";
5+
import { PdfHeader } from "~/features/pdf/Header";
6+
import { HeaderStyles } from "~/features/pdf/PdfStyles";
7+
import { TableOfContentsPdf } from "~/features/pdf/TableOfContents";
8+
import { TitlePage } from "~/features/pdf/TitlePage";
9+
import { PdfWrapper } from "~/features/pdf/Wrapper";
10+
import { AggregateStatisticsResponse } from "~/types/api";
11+
import { ClassificationBreakdownItem } from "~/types/api/models/ClassificationBreakdownItem";
12+
import { VendorBreakdownItem } from "~/types/api/models/VendorBreakdownItem";
13+
14+
import { MonitorReportDataElements } from "./sections/MonitorReportDataElements";
15+
import { MonitorReportSummary } from "./sections/MonitorReportSummary";
16+
import { MonitorReportsThirdParties } from "./sections/MonitorReportThirdParties";
17+
18+
const styles = StyleSheet.create({
19+
page: {
20+
paddingHorizontal: 40,
21+
paddingTop: 40,
22+
flexDirection: "column",
23+
backgroundColor: palette.FIDESUI_BG_WHITE,
24+
justifyContent: "space-evenly",
25+
},
26+
section: {
27+
display: "flex",
28+
flexDirection: "column",
29+
gap: 10,
30+
fontFamily: "BasierSquare",
31+
fontSize: 12,
32+
marginVertical: 10,
33+
paddingVertical: 10,
34+
flexGrow: 1,
35+
},
36+
});
37+
38+
const SECTIONS = [
39+
{
40+
title: "Summary",
41+
description:
42+
"Dashboard of detected systems, scripts, cookies, and consent issues.",
43+
},
44+
{
45+
title: "Third Parties",
46+
description: "All vendors observed, categorized with data they receive.",
47+
},
48+
{
49+
title: "Data Elements",
50+
description:
51+
"Fides data-use taxonomy labels and the vendors receiving each.",
52+
},
53+
] as const;
54+
55+
export const MonitorReport = ({
56+
author,
57+
classifications,
58+
classificationsChart,
59+
imageSrc,
60+
location,
61+
monitorTitle,
62+
stats,
63+
vendors,
64+
vendorsChart,
65+
}: {
66+
author: string;
67+
classifications: Array<ClassificationBreakdownItem>;
68+
classificationsChart: string;
69+
imageSrc: string;
70+
location: string;
71+
monitorTitle: string;
72+
stats: AggregateStatisticsResponse;
73+
vendors: Array<VendorBreakdownItem>;
74+
vendorsChart: string;
75+
}) => (
76+
<PdfWrapper>
77+
<TitlePage
78+
imageString={imageSrc}
79+
monitorTitle={monitorTitle}
80+
location={location}
81+
author={author}
82+
/>
83+
<Page size="A4" style={styles.page}>
84+
<PdfHeader title="Ethyca" />
85+
<View style={styles.section}>
86+
<Text style={HeaderStyles.h4}>Contents</Text>
87+
<Text style={HeaderStyles.h1}>Report details</Text>
88+
<Text style={[HeaderStyles.h4, { fontSize: 12 }]}>
89+
Table of contents
90+
</Text>
91+
<TableOfContentsPdf sections={SECTIONS} />
92+
</View>
93+
<PdfFooter />
94+
</Page>
95+
{SECTIONS.map(({ title }, sectionIndex) => (
96+
<Page size="A4" style={styles.page} key={title}>
97+
<PdfHeader
98+
section={{ title, index: sectionIndex + 1 }}
99+
title="Ethyca"
100+
/>
101+
<View style={styles.section}>
102+
<Text style={HeaderStyles.h5}>{`Chapter ${sectionIndex + 1}`}</Text>
103+
<Text style={HeaderStyles.h1}>{title}</Text>
104+
{title === "Summary" && (
105+
<MonitorReportSummary
106+
classifications={classifications}
107+
vendors={vendors}
108+
stats={stats}
109+
monitorTitle={monitorTitle}
110+
/>
111+
)}
112+
{title === "Data Elements" && (
113+
<MonitorReportDataElements
114+
classifications={classifications}
115+
classificationsChart={classificationsChart}
116+
/>
117+
)}
118+
{title === "Third Parties" && (
119+
<MonitorReportsThirdParties
120+
vendors={vendors}
121+
vendorsChart={vendorsChart}
122+
/>
123+
)}
124+
</View>
125+
<PdfFooter />
126+
</Page>
127+
))}
128+
</PdfWrapper>
129+
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Button, Icons, Tooltip } from "fidesui";
2+
import { useRouter } from "next/router";
3+
4+
import { useMonitorReportDownload } from "./useMonitorReportDownload";
5+
6+
const MonitorReportDownload = () => {
7+
const router = useRouter();
8+
const monitorId = decodeURIComponent(router.query.monitorId as string);
9+
const { isGenerating, generate } = useMonitorReportDownload(monitorId);
10+
11+
return (
12+
<Tooltip title="Generate Report">
13+
<Button
14+
aria-label="Generate Report"
15+
icon={<Icons.Document />}
16+
onClick={() => generate()}
17+
loading={isGenerating}
18+
disabled={isGenerating}
19+
/>
20+
</Tooltip>
21+
);
22+
};
23+
24+
export default MonitorReportDownload;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Pie, PieChart } from "recharts";
2+
3+
import { VISUAL_COLORS } from "~/features/pdf/PdfStyles";
4+
import { ClassificationBreakdownItem } from "~/types/api/models/ClassificationBreakdownItem";
5+
6+
const MonitorReportClassificationsChart = ({
7+
classifications,
8+
}: {
9+
classifications: Array<ClassificationBreakdownItem>;
10+
}) => (
11+
<PieChart
12+
barCategoryGap="10%"
13+
barGap={4}
14+
height={1200}
15+
width={1200}
16+
responsive
17+
style={{ aspectRatio: 1 }}
18+
endAngle={360}
19+
startAngle={0}
20+
>
21+
<Pie
22+
data={classifications?.map(({ vendors, name }, i) => ({
23+
vendorsCount: vendors?.length,
24+
name,
25+
fill: VISUAL_COLORS[i],
26+
}))}
27+
dataKey="vendorsCount"
28+
nameKey="name"
29+
isAnimationActive={false}
30+
innerRadius="30%"
31+
/>
32+
</PieChart>
33+
);
34+
35+
export default MonitorReportClassificationsChart;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Pie, PieChart } from "recharts";
2+
3+
import { VISUAL_COLORS } from "~/features/pdf/PdfStyles";
4+
import { VendorBreakdownItem } from "~/types/api/models/VendorBreakdownItem";
5+
6+
const MonitorReportVendorsChart = ({
7+
vendors,
8+
}: {
9+
vendors: Array<VendorBreakdownItem>;
10+
}) => (
11+
<PieChart
12+
barCategoryGap="10%"
13+
barGap={4}
14+
height={1200}
15+
width={1200}
16+
responsive
17+
style={{ aspectRatio: 1 }}
18+
endAngle={360}
19+
startAngle={0}
20+
>
21+
<Pie
22+
data={vendors?.map(({ resource_count, name }, i) => ({
23+
resource_count,
24+
name,
25+
fill: VISUAL_COLORS[i],
26+
}))}
27+
dataKey="resource_count"
28+
nameKey="name"
29+
isAnimationActive={false}
30+
innerRadius="30%"
31+
/>
32+
</PieChart>
33+
);
34+
35+
export default MonitorReportVendorsChart;

0 commit comments

Comments
 (0)