import { Button, message, Spin } from 'antd'; import React, { useMemo } from 'react'; import { Column, ColumnConfig } from '@ant-design/charts'; import { SyncOutlined } from '@ant-design/icons'; import { DateFilter } from '../DateFilter'; import { StatsItemType, useWorkspaceWebsitePageview, useWorkspaceWebsiteStats, WebsiteInfo, } from '../../api/model/website'; 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'; import { WebsiteOnlineCount } from '../WebsiteOnlineCount'; import { useGlobalRangeDate } from '../../hooks/useGlobalRangeDate'; import { MonitorHealthBar } from '../monitor/MonitorHealthBar'; import { useNavigate } from 'react-router'; export const WebsiteOverview: React.FC<{ website: WebsiteInfo; showDateFilter?: boolean; actions?: React.ReactNode; }> = React.memo((props) => { const { website, showDateFilter = false, actions } = props; const { startDate, endDate, unit, refresh } = useGlobalRangeDate(); const navigate = useNavigate(); const { pageviews, sessions, isLoading: isLoadingPageview, refetch: refetchPageview, } = useWorkspaceWebsitePageview( website.workspaceId, website.id, startDate.unix() * 1000, endDate.unix() * 1000, unit ); const { stats, isLoading: isLoadingStats, refetch: refetchStats, } = useWorkspaceWebsiteStats( website.workspaceId, website.id, startDate.unix() * 1000, endDate.unix() * 1000, unit ); const handleRefresh = useEvent(async () => { refresh(); 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]); const loading = isLoadingPageview || isLoadingStats; return (
{website.name} {website.monitorId && (
navigate(`/monitor/${website.monitorId}`)} >
)}
{actions}
{stats && }
); }); WebsiteOverview.displayName = 'WebsiteOverview'; 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), }, }, } satisfies ColumnConfig), [props.data, props.unit] ); return ; }); StatsChart.displayName = 'StatsChart';