Updated reports components.

This commit is contained in:
Mike Cao 2025-03-26 21:54:23 -07:00
parent f5bc3dc6c2
commit 0f6cdf8b80
95 changed files with 580 additions and 698 deletions

View file

@ -9,18 +9,12 @@ import {
useFilters,
} from '@/components/hooks';
import { FieldFilterEditForm } from '@/app/(main)/reports/[reportId]/FieldFilterEditForm';
import { OPERATOR_PREFIXES } from '@/lib/constants';
import { FILTER_COLUMNS, OPERATOR_PREFIXES } from '@/lib/constants';
import { isSearchOperator, parseParameterValue } from '@/lib/params';
import styles from './FilterTags.module.css';
import { WebsiteFilterButton } from '@/app/(main)/websites/[websiteId]/WebsiteFilterButton';
export function FilterTags({
websiteId,
params,
}: {
websiteId: string;
params: { [key: string]: string };
}) {
export function FilterTags({ websiteId }: { websiteId: string }) {
const { formatMessage, labels } = useMessages();
const { formatValue } = useFormat();
const { dateRange } = useDateRange(websiteId);
@ -32,6 +26,14 @@ export function FilterTags({
const { fields } = useFields();
const { operatorLabels } = useFilters();
const { startDate, endDate } = dateRange;
const { query } = useNavigation();
const params = Object.keys(query).reduce((obj, key) => {
if (FILTER_COLUMNS[key]) {
obj[key] = query[key];
}
return obj;
}, {});
if (Object.keys(params).filter(key => params[key]).length === 0) {
return null;
@ -60,13 +62,13 @@ export function FilterTags({
return (
<Row
gap="3"
backgroundColor="1"
backgroundColor="2"
alignItems="center"
paddingX="3"
paddingY="2"
paddingX="5"
paddingY="3"
border
borderRadius="2"
borderSize="1"
marginBottom="6"
wrap="wrap"
>
<Text weight="bold">{formatMessage(labels.filters)}</Text>
{Object.keys(params).map(key => {

View file

@ -1,37 +1,8 @@
.card {
display: flex;
flex-direction: column;
justify-content: center;
min-width: 150px;
}
.card.compare .change {
font-size: 16px;
margin: 10px 0;
}
.card:first-child {
padding-left: 0;
border-right: 1px solid var(--border-color);
padding: 0 50px;
}
.card:last-child {
border: 0;
}
.value {
font-size: 36px;
font-weight: 700;
white-space: nowrap;
color: var(--base900);
line-height: 1.5;
}
.value.prev {
color: var(--base800);
}
.label {
font-weight: 700;
white-space: nowrap;
color: var(--base800);
border-right: 0;
}

View file

@ -1,4 +1,4 @@
import classNames from 'classnames';
import { Text, Column } from '@umami/react-zen';
import { useSpring } from '@react-spring/web';
import { formatNumber } from '@/lib/format';
import { AnimatedDiv } from '@/components/common/AnimatedDiv';
@ -15,7 +15,6 @@ export interface MetricCardProps {
showLabel?: boolean;
showChange?: boolean;
showPrevious?: boolean;
className?: string;
}
export const MetricCard = ({
@ -27,7 +26,6 @@ export const MetricCard = ({
showLabel = true,
showChange = false,
showPrevious = false,
className,
}: MetricCardProps) => {
const diff = value - change;
const pct = ((value - diff) / diff) * 100;
@ -36,26 +34,19 @@ export const MetricCard = ({
const prevProps = useSpring({ x: Number(diff) || 0, from: { x: 0 } });
return (
<div className={classNames(styles.card, className, showPrevious && styles.compare)}>
{showLabel && <div className={styles.label}>{label}</div>}
<AnimatedDiv className={styles.value} title={value?.toString()}>
{props?.x?.to(x => formatValue(x))}
</AnimatedDiv>
<Column className={styles.card} justifyContent="center">
{showLabel && <Text weight="bold">{label}</Text>}
<Text size="8" weight="bold" wrap="nowrap">
<AnimatedDiv title={value?.toString()}>{props?.x?.to(x => formatValue(x))}</AnimatedDiv>
</Text>
{showChange && (
<ChangeLabel
className={styles.change}
value={change}
title={formatValue(change)}
reverseColors={reverseColors}
>
<ChangeLabel value={change} title={formatValue(change)} reverseColors={reverseColors}>
<AnimatedDiv>{changeProps?.x?.to(x => `${Math.abs(~~x)}%`)}</AnimatedDiv>
</ChangeLabel>
)}
{showPrevious && (
<AnimatedDiv className={classNames(styles.value, styles.prev)} title={diff.toString()}>
{prevProps?.x?.to(x => formatValue(x))}
</AnimatedDiv>
<AnimatedDiv title={diff.toString()}>{prevProps?.x?.to(x => formatValue(x))}</AnimatedDiv>
)}
</div>
</Column>
);
};

View file

@ -1,13 +0,0 @@
.bar {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, max-content));
gap: 20px;
width: 100%;
position: relative;
}
@media screen and (max-width: 768px) {
.bar {
grid-template-columns: 1fr 1fr;
}
}

View file

@ -1,8 +1,6 @@
import { ReactNode } from 'react';
import { Loading, Row } from '@umami/react-zen';
import { cloneChildren } from '@/lib/react';
import { Grid, Loading } from '@umami/react-zen';
import { ErrorMessage } from '@/components/common/ErrorMessage';
import { formatLongNumber } from '@/lib/format';
export interface MetricsBarProps {
isLoading?: boolean;
@ -12,18 +10,15 @@ export interface MetricsBarProps {
}
export function MetricsBar({ children, isLoading, isFetched, error }: MetricsBarProps) {
const formatFunc = n => (n >= 0 ? formatLongNumber(n) : `-${formatLongNumber(Math.abs(n))}`);
return (
<Row>
<>
{isLoading && !isFetched && <Loading icon="dots" />}
{error && <ErrorMessage />}
{!isLoading &&
!error &&
isFetched &&
cloneChildren(children, child => {
return { format: child?.props['format'] || formatFunc };
})}
</Row>
{!isLoading && !error && isFetched && (
<Grid columns="repeat(auto-fill, minmax(200px, 1fr))" width="100%" gapY="3">
{children}
</Grid>
)}
</>
);
}