From 71272c982ec65b5adffa645205e4c513f4405a16 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Fri, 11 Aug 2023 20:38:13 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20code=20structure=20of?= =?UTF-8?q?=20dns=20hole=20summary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/_app.tsx | 93 +++++++-------- src/tools/client/math.ts | 4 + src/widgets/dnshole/DnsHoleSummary.tsx | 153 +++++++++++++------------ 3 files changed, 129 insertions(+), 121 deletions(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 8025ee4da..bcfcf4e42 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -109,57 +109,52 @@ function App( - - - - + + - - - - - - - - - - - + Switch: { + styles: { + input: { cursor: 'pointer' }, + label: { cursor: 'pointer' }, + }, + }, + }, + primaryColor, + primaryShade, + colorScheme, + }} + withGlobalStyles + withNormalizeCSS + > + + + + + + + + + + ); } diff --git a/src/tools/client/math.ts b/src/tools/client/math.ts index ec2db7723..1b5e4de94 100644 --- a/src/tools/client/math.ts +++ b/src/tools/client/math.ts @@ -16,3 +16,7 @@ export const formatNumber = (n: number, decimalPlaces: number) => { } return n.toFixed(decimalPlaces); }; + +export const formatPercentage = (n: number, decimalPlaces: number) => { + return `${(n * 100).toFixed(decimalPlaces)}%`; +}; diff --git a/src/widgets/dnshole/DnsHoleSummary.tsx b/src/widgets/dnshole/DnsHoleSummary.tsx index fd1c9f75a..6a525eb00 100644 --- a/src/widgets/dnshole/DnsHoleSummary.tsx +++ b/src/widgets/dnshole/DnsHoleSummary.tsx @@ -1,4 +1,4 @@ -import { Card, Center, Container, Flex, Text } from '@mantine/core'; +import { Box, Card, Center, Container, Flex, Text } from '@mantine/core'; import { useElementSize } from '@mantine/hooks'; import { IconAd, @@ -6,17 +6,21 @@ import { IconPercentage, IconSearch, IconWorldWww, + TablerIconsProps, } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; import React from 'react'; import { useConfigContext } from '~/config/provider'; -import { api } from '~/utils/api'; +import { RouterOutputs, api } from '~/utils/api'; -import { formatNumber } from '../../tools/client/math'; +import { formatNumber, formatPercentage } from '../../tools/client/math'; import { defineWidget } from '../helper'; import { WidgetLoading } from '../loading'; import { IWidget } from '../widgets'; +const availableLayouts = ['grid', 'row', 'column'] as const; +type AvailableLayout = (typeof availableLayouts)[number]; + const definition = defineWidget({ id: 'dns-hole-summary', icon: IconAd, @@ -27,8 +31,8 @@ const definition = defineWidget({ }, layout: { type: 'select', - defaultValue: 'grid', - data: [{ value: 'grid' }, { value: 'row' }, { value: 'column' }], + defaultValue: 'grid' as AvailableLayout, + data: availableLayouts.map((x) => ({ value: x })), }, }, gridstack: { @@ -47,60 +51,54 @@ interface DnsHoleSummaryWidgetProps { } function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) { - const { t } = useTranslation('modules/dns-hole-summary'); const { isInitialLoading, data } = useDnsHoleSummeryQuery(); - const flexLayout = widget.properties.layout as 'row' | 'column'; if (isInitialLoading || !data) { return ; } return ( - - } - number={formatNumber(data.adsBlockedToday, 2)} - label={t('card.metrics.queriesBlockedToday') as string} - color={ - widget.properties.usePiHoleColors ? 'rgba(240, 82, 60, 0.4)' : 'rgba(96, 96, 96, 0.1)' - } - /> - } - number={(data.adsBlockedTodayPercentage * 100).toFixed(2) + '%'} - color={ - widget.properties.usePiHoleColors ? 'rgba(255, 165, 20, 0.4)' : 'rgba(96, 96, 96, 0.1)' - } - /> - } - number={formatNumber(data.dnsQueriesToday, 2)} - label={t('card.metrics.queriesToday') as string} - color={ - widget.properties.usePiHoleColors ? 'rgba(0, 175, 218, 0.4)' : 'rgba(96, 96, 96, 0.1)' - } - /> - } - number={formatNumber(data.domainsBeingBlocked, 2)} - label={t('card.metrics.domainsOnAdlist') as string} - color={ - widget.properties.usePiHoleColors ? 'rgba(0, 176, 96, 0.4)' : 'rgba(96, 96, 96, 0.1)' - } - /> + + {stats.map((item) => ( + + ))} ); } +const stats = [ + { + icon: IconBarrierBlock, + value: (x) => formatNumber(x.adsBlockedToday, 2), + label: 'card.metrics.queriesBlockedToday', + color: 'rgba(240, 82, 60, 0.4)', + }, + { + icon: IconPercentage, + value: (x) => formatPercentage(x.adsBlockedTodayPercentage, 2), + color: 'rgba(255, 165, 20, 0.4)', + }, + { + icon: IconSearch, + value: (x) => formatNumber(x.dnsQueriesToday, 2), + label: 'card.metrics.queriesToday', + color: 'rgba(0, 175, 218, 0.4)', + }, + { + icon: IconWorldWww, + value: (x) => formatNumber(x.domainsBeingBlocked, 2), + label: 'card.metrics.domainsOnAdlist', + color: 'rgba(0, 176, 96, 0.4)', + }, +] satisfies StatItem[]; + +type StatItem = { + icon: (props: TablerIconsProps) => JSX.Element; + value: (x: RouterOutputs['dnsHole']['summary']) => string; + label?: string; + color: string; +}; + export const useDnsHoleSummeryQuery = () => { const { name: configName } = useConfigContext(); @@ -114,23 +112,23 @@ export const useDnsHoleSummeryQuery = () => { ); }; -interface StatCardProps { - icon: JSX.Element; - number: string; - label?: string; - color?: string; -} - -const StatCard = ({ icon, number, label, color }: StatCardProps) => { +type StatCardProps = { + item: StatItem; + data: RouterOutputs['dnsHole']['summary']; + usePiHoleColors: boolean; +}; +const StatCard = ({ item, data, usePiHoleColors }: StatCardProps) => { + const { t } = useTranslation('modules/dns-hole-summary'); const { ref, height, width } = useElementSize(); + return ( @@ -142,31 +140,42 @@ const StatCard = ({ icon, number, label, color }: StatCardProps) => { justify="space-evenly" direction={width > height + 20 ? 'row' : 'column'} > - {React.cloneElement(icon, { - size: 30, - style: { margin: '0 10' } - })} -
+ - {number} + {item.value(data)} - {label && ( + {item.label && ( - {label} + {t(item.label)} )} -
+
); }; +const constructContainerStyle = (flexLayout: (typeof availableLayouts)[number]) => { + if (flexLayout === 'grid') { + return { + display: 'grid', + gridTemplateColumns: '1fr 1fr', + gridTemplateRows: '1fr 1fr', + }; + } + + return { + display: 'flex', + flexDirection: flexLayout, + }; +}; + export default definition;