import { Box, Card, Center, Container, Flex, Text } from '@mantine/core'; import { useElementSize } from '@mantine/hooks'; import { IconAd, IconBarrierBlock, IconPercentage, IconSearch, IconWorldWww, TablerIconsProps, } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '~/config/provider'; import { RouterOutputs, api } from '~/utils/api'; 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, options: { usePiHoleColors: { type: 'switch', defaultValue: true, }, layout: { type: 'select', defaultValue: 'grid' as AvailableLayout, data: availableLayouts.map((x) => ({ value: x })), }, }, gridstack: { minWidth: 2, minHeight: 1, maxWidth: 12, maxHeight: 12, }, component: DnsHoleSummaryWidgetTile, }); export type IDnsHoleSummaryWidget = IWidget<(typeof definition)['id'], typeof definition>; interface DnsHoleSummaryWidgetProps { widget: IDnsHoleSummaryWidget; } function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) { const { isInitialLoading, data } = useDnsHoleSummeryQuery(); if (isInitialLoading || !data) { return ; } return ( {stats.map((item, index) => ( ))} ); } 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), label: 'card.metrics.queriesBlockedTodayPercentage', 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(); return api.dnsHole.summary.useQuery( { configName: configName!, }, { staleTime: 1000 * 60 * 2, } ); }; 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(); const isLong = width > height + 20; return (
{item.value(data)} {item.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;