From a14a9d46011f0e28586fbaa8b950cce84a03a900 Mon Sep 17 00:00:00 2001 From: Tagaishi Date: Thu, 10 Aug 2023 23:49:55 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=92=84=20Flex=20layout=20and=20text?= =?UTF-8?q?=20fitting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../locales/en/modules/dns-hole-summary.json | 8 + src/widgets/dnshole/DnsHoleSummary.tsx | 223 ++++++++---------- 2 files changed, 107 insertions(+), 124 deletions(-) diff --git a/public/locales/en/modules/dns-hole-summary.json b/public/locales/en/modules/dns-hole-summary.json index 207d37e1f..17c1149a6 100644 --- a/public/locales/en/modules/dns-hole-summary.json +++ b/public/locales/en/modules/dns-hole-summary.json @@ -6,6 +6,14 @@ "title": "Settings for DNS Hole summary", "usePiHoleColors": { "label": "Use colors from PiHole" + }, + "layout": { + "label": "Layout", + "data": { + "grid": "2 by 2", + "row": "Horizontal", + "column": "Vertical" + } } } }, diff --git a/src/widgets/dnshole/DnsHoleSummary.tsx b/src/widgets/dnshole/DnsHoleSummary.tsx index bcfc443e5..fd1c9f75a 100644 --- a/src/widgets/dnshole/DnsHoleSummary.tsx +++ b/src/widgets/dnshole/DnsHoleSummary.tsx @@ -1,4 +1,5 @@ -import { Card, Center, Container, Stack, Text } from '@mantine/core'; +import { Card, Center, Container, Flex, Text } from '@mantine/core'; +import { useElementSize } from '@mantine/hooks'; import { IconAd, IconBarrierBlock, @@ -7,6 +8,7 @@ import { IconWorldWww, } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; +import React from 'react'; import { useConfigContext } from '~/config/provider'; import { api } from '~/utils/api'; @@ -23,10 +25,15 @@ const definition = defineWidget({ type: 'switch', defaultValue: true, }, + layout: { + type: 'select', + defaultValue: 'grid', + data: [{ value: 'grid' }, { value: 'row' }, { value: 'column' }], + }, }, gridstack: { minWidth: 2, - minHeight: 2, + minHeight: 1, maxWidth: 12, maxHeight: 12, }, @@ -42,6 +49,7 @@ 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 ; @@ -49,134 +57,46 @@ function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) { return ( - { - if (!widget.properties.usePiHoleColors) { - return {}; - } - - if (theme.colorScheme === 'dark') { - return { - backgroundColor: 'rgba(240, 82, 60, 0.4)', - }; - } - - return { - backgroundColor: 'rgba(240, 82, 60, 0.2)', - }; - }} - withBorder - > -
- - -
- {formatNumber(data.adsBlockedToday, 0)} - - {t('card.metrics.queriesBlockedToday')} - -
-
-
-
- { - if (!widget.properties.usePiHoleColors) { - return {}; - } - - if (theme.colorScheme === 'dark') { - return { - backgroundColor: 'rgba(255, 165, 20, 0.4)', - }; - } - - return { - backgroundColor: 'rgba(255, 165, 20, 0.4)', - }; - }} - withBorder - > -
- - - {(data.adsBlockedTodayPercentage * 100).toFixed(2)}% - -
-
- { - if (!widget.properties.usePiHoleColors) { - return {}; - } - - if (theme.colorScheme === 'dark') { - return { - backgroundColor: 'rgba(0, 175, 218, 0.4)', - }; - } - - return { - backgroundColor: 'rgba(0, 175, 218, 0.4)', - }; - }} - withBorder - > -
- - -
- {formatNumber(data.dnsQueriesToday, 3)} - - {t('card.metrics.queriesToday')} - -
-
-
-
- { - if (!widget.properties.usePiHoleColors) { - return {}; - } - - if (theme.colorScheme === 'dark') { - return { - backgroundColor: 'rgba(0, 176, 96, 0.4)', - }; - } - - return { - backgroundColor: 'rgba(0, 176, 96, 0.4)', - }; - }} - withBorder - > -
- - -
- {formatNumber(data.domainsBeingBlocked, 0)} - - {t('card.metrics.domainsOnAdlist')} - -
-
-
-
+ } + 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)' + } + />
); } @@ -194,4 +114,59 @@ export const useDnsHoleSummeryQuery = () => { ); }; +interface StatCardProps { + icon: JSX.Element; + number: string; + label?: string; + color?: string; +} + +const StatCard = ({ icon, number, label, color }: StatCardProps) => { + const { ref, height, width } = useElementSize(); + return ( + +
+ height + 20 ? 'row' : 'column'} + > + {React.cloneElement(icon, { + size: 30, + style: { margin: '0 10' } + })} +
+ + {number} + + {label && ( + + {label} + + )} +
+
+
+
+ ); +}; + export default definition; From 71272c982ec65b5adffa645205e4c513f4405a16 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Fri, 11 Aug 2023 20:38:13 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20code=20structure?= =?UTF-8?q?=20of=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; From d14834d517dd20c623ae98e0476696d71822c762 Mon Sep 17 00:00:00 2001 From: Tagaishi Date: Sat, 12 Aug 2023 13:16:58 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20remove=20icon=20alignment=20?= =?UTF-8?q?on=20column=20stat=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/dnshole/DnsHoleSummary.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/widgets/dnshole/DnsHoleSummary.tsx b/src/widgets/dnshole/DnsHoleSummary.tsx index 6a525eb00..9b25e5efd 100644 --- a/src/widgets/dnshole/DnsHoleSummary.tsx +++ b/src/widgets/dnshole/DnsHoleSummary.tsx @@ -9,7 +9,6 @@ import { TablerIconsProps, } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; -import React from 'react'; import { useConfigContext } from '~/config/provider'; import { RouterOutputs, api } from '~/utils/api'; @@ -120,6 +119,7 @@ type StatCardProps = { const StatCard = ({ item, data, usePiHoleColors }: StatCardProps) => { const { t } = useTranslation('modules/dns-hole-summary'); const { ref, height, width } = useElementSize(); + const isLong = width > height + 20; return ( { w="100%" align="center" justify="space-evenly" - direction={width > height + 20 ? 'row' : 'column'} + direction={isLong ? 'row' : 'column'} >