import { Button, message, Tag } from 'antd'; import React, { useMemo } from 'react'; import { Column, ColumnConfig } from '@ant-design/charts'; import { ArrowRightOutlined, SyncOutlined } from '@ant-design/icons'; import { DateFilter } from './DateFilter'; import { HealthBar } from './HealthBar'; import { StatsItemType, useWorkspaceWebsitePageview, useWorkspaceWebsiteStats, useWorspaceWebsites, WebsiteInfo, } from '../api/model/website'; import { Loading } from './Loading'; import dayjs from 'dayjs'; import { DateUnit, formatDate, formatDateWithUnit, getDateArray, } from '../utils/date'; import { useEvent } from '../hooks/useEvent'; import { MetricCard } from './MetricCard'; import { formatNumber, formatShortTime } from '../utils/common'; import { useTheme } from '../hooks/useTheme'; interface WebsiteOverviewProps { workspaceId: string; } export const WebsiteOverview: React.FC = React.memo( (props) => { const { isLoading, websites } = useWorspaceWebsites(props.workspaceId); if (isLoading) { return ; } return (
{websites.map((website) => ( ))}
); } ); WebsiteOverview.displayName = 'WebsiteOverview'; const WebsiteOverviewItem: React.FC<{ website: WebsiteInfo; }> = React.memo((props) => { const unit: DateUnit = 'hour'; const startDate = dayjs().subtract(1, 'day').add(1, unit).startOf(unit); const endDate = dayjs().endOf(unit); const { pageviews, sessions, isLoading: isLoadingPageview, refetch: refetchPageview, } = useWorkspaceWebsitePageview( props.website.workspaceId, props.website.id, startDate.unix() * 1000, endDate.unix() * 1000, unit ); const { stats, isLoading: isLoadingStats, refetch: refetchStats, } = useWorkspaceWebsiteStats( props.website.workspaceId, props.website.id, startDate.unix() * 1000, endDate.unix() * 1000, unit ); const handleRefresh = useEvent(async () => { await Promise.all([refetchPageview(), refetchStats()]); message.success('Refreshed'); }); const chartData = useMemo(() => { const pageviewsArr = getDateArray(pageviews, startDate, endDate, unit); const sessionsArr = getDateArray(sessions, startDate, endDate, unit); return [ ...pageviewsArr.map((item) => ({ ...item, type: 'pageview' })), ...sessionsArr.map((item) => ({ ...item, type: 'session' })), ]; }, [pageviews, sessions, unit]); if (isLoadingPageview || isLoadingStats) { return ; } return (
{props.website.name} ({ status: 'health', }))} />
{stats && }
); }); WebsiteOverviewItem.displayName = 'WebsiteOverviewItem'; export const MetricsBar: React.FC<{ stats: { bounces: StatsItemType; pageviews: StatsItemType; totaltime: StatsItemType; uniques: StatsItemType; }; }> = React.memo((props) => { const { pageviews, uniques, bounces, totaltime } = props.stats || {}; const num = Math.min(uniques.value, bounces.value); const diffs = { pageviews: pageviews.value - pageviews.change, uniques: uniques.value - uniques.change, bounces: bounces.value - bounces.change, totaltime: totaltime.value - totaltime.change, }; return (
formatNumber(n) + '%'} /> `${n < 0 ? '-' : ''}${formatShortTime( Math.abs(~~n), ['m', 's'], ' ' )}` } />
); }); MetricsBar.displayName = 'MetricsBar'; export const StatsChart: React.FC<{ data: { x: string; y: number; type: string }[]; unit: DateUnit; }> = React.memo((props) => { const { colors } = useTheme(); const config = useMemo( () => ({ data: props.data, isStack: true, xField: 'x', yField: 'y', seriesField: 'type', label: { position: 'middle' as const, style: { fill: '#FFFFFF', opacity: 0.6, }, }, tooltip: { title: (t) => formatDate(t), }, color: [colors.chart.pv, colors.chart.uv], xAxis: { label: { autoHide: true, autoRotate: false, formatter: (text) => formatDateWithUnit(text, props.unit), }, }, } as ColumnConfig), [props.data, props.unit] ); return ; }); StatsChart.displayName = 'StatsChart';