Update annotation date picker and place annotation actions beside chart legend

This commit is contained in:
Mike Cao committed 2026-09-13 22:30:30 -07:00
1 parent fed1ff76f6
commit 5f92ec5c49
7 files changed
+43 -70

No files matched your search

+1 -1
View File
@@ -83,7 +83,7 @@
"@tanstack/react-query": "^5.102.8",
"@umami/api-client": "workspace:*",
"@umami/mcp": "workspace:*",
"@umami/react-zen": "^0.253.0",
"@umami/react-zen": "^0.254.0",
"bcryptjs": "^3.0.2",
"chalk": "^5.6.2",
"chart.js": "^4.5.1",
+5 -5
View File
@@ -62,8 +62,8 @@ importers:
specifier: workspace:*
version: link:packages/mcp
'@umami/react-zen':
specifier: ^0.253.0
version: 0.253.0(@date-fns/tz@1.5.0)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(date-fns@4.4.0)(immer@11.1.18)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.3.0))
specifier: ^0.254.0
version: 0.254.0(@date-fns/tz@1.5.0)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(date-fns@4.4.0)(immer@11.1.18)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.3.0))
bcryptjs:
specifier: ^3.0.2
version: 3.0.3
@@ -2774,8 +2774,8 @@ packages:
'@umami/api-client@0.81.0':
resolution: {integrity: sha512-cwkQbe8U/m+zEgfe+nNExU0AoY8BSd+x4Qs7i5nfR2gGrktfoH6Y375e237OFYpmvhJr5BJdML1Vzuu6v7+kmw==}
'@umami/react-zen@0.253.0':
resolution: {integrity: sha512-ilprZZ+CM+ryHYcNFJKh2+DJFdnyby2y8UPa458e9hG5dXTtv5qpnhVcr6xSTxBfZqoMSC992vcXbWsn+PhjRg==}
'@umami/react-zen@0.254.0':
resolution: {integrity: sha512-1Ip3C3UuL+ltBTjGmLLB5y1IgxwNBtxYY8z/jkYyfVIpX3mNMP6NKdQIqZGHgrbgcq7hj88dslfD9Gc2SzJk1w==}
peerDependencies:
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
@@ -7955,7 +7955,7 @@ snapshots:
'@umami/api-client@0.81.0': {}
'@umami/react-zen@0.253.0(@date-fns/tz@1.5.0)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(date-fns@4.4.0)(immer@11.1.18)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.3.0))':
'@umami/react-zen@0.254.0(@date-fns/tz@1.5.0)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(date-fns@4.4.0)(immer@11.1.18)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.3.0))':
dependencies:
'@base-ui/react': 1.8.0(@date-fns/tz@1.5.0)(@types/react@19.3.0)(date-fns@4.4.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
'@react-spring/web': 9.7.5(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
@@ -1,5 +1,5 @@
import { isSameDay } from 'date-fns';
import { useCallback, useMemo } from 'react';
import { type ReactNode, useCallback, useMemo } from 'react';
import type { ChartAnnotation } from '@/components/charts/ChartAnnotationMarkers';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import {
@@ -19,11 +19,13 @@ export function WebsiteChart({
compareMode,
showAnnotations,
onAnnotationMoreClick,
legendActions,
}: {
websiteId: string;
compareMode?: boolean;
showAnnotations?: boolean;
onAnnotationMoreClick?: (range: AnnotationRange) => void;
legendActions?: ReactNode;
}) {
const { timezone, localFromUtc, localToUtc } = useTimezone();
const { dateRange, dateCompare } = useDateRange({ timezone: timezone });
@@ -135,6 +137,7 @@ export function WebsiteChart({
<PageviewsChart
key={value}
data={chartData}
legendActions={legendActions}
minDate={startDate}
maxDate={endDate}
unit={unit}
@@ -4,8 +4,8 @@ import { useState } from 'react';
import { ExpandedViewModal } from '@/app/(main)/websites/[websiteId]/ExpandedViewModal';
import { Panel } from '@/components/common/Panel';
import { DialogButton } from '@/components/input/DialogButton';
import type { AnnotationRange } from '@/lib/annotations';
import { UnitFilter } from '@/components/input/UnitFilter';
import type { AnnotationRange } from '@/lib/annotations';
import { AnnotationsButton } from './annotations/AnnotationsButton';
import { AnnotationsModal } from './annotations/AnnotationsModal';
import { WebsiteChart } from './WebsiteChart';
@@ -28,10 +28,8 @@ export function WebsitePage({ websiteId }: { websiteId: string }) {
websiteId={websiteId}
showAnnotations
onAnnotationMoreClick={setAnnotationRange}
legendActions={<AnnotationsButton websiteId={websiteId} />}
/>
<Row justifyContent="end">
<AnnotationsButton websiteId={websiteId} />
</Row>
</Panel>
<WebsitePanels websiteId={websiteId} />
<ExpandedViewModal websiteId={websiteId} />
@@ -1,23 +1,17 @@
import {
Button,
Calendar as ZenCalendar,
Column,
DialogTrigger,
DatePicker,
Form,
FormButtons,
FormField,
FormSubmitButton,
Icon,
Label,
Popover,
Text,
TextField,
} from '@umami/react-zen';
import { startOfDay } from 'date-fns';
import { useMemo, useState } from 'react';
import { useLocale, useMessages, useTimezone, useUpdateQuery } from '@/components/hooks';
import { Calendar } from '@/components/icons';
import { formatDate } from '@/lib/date';
export interface Annotation {
id: string;
@@ -27,51 +21,6 @@ export interface Annotation {
note: string;
}
function AnnotationDatePicker({
date,
label,
locale,
onChange,
}: {
date: Date;
label: string;
locale: string;
onChange: (date: Date) => void;
}) {
const [isOpen, setIsOpen] = useState(false);
const handleChange = (value: Date) => {
onChange(value);
setIsOpen(false);
};
return (
<Column gap="1">
<Label>{label}</Label>
<DialogTrigger overlayType="popover" isOpen={isOpen} onOpenChange={setIsOpen}>
<Button
variant="outline"
aria-label={label}
style={{ width: 'fit-content', cursor: 'pointer', flexDirection: 'row-reverse' }}
>
<Icon size="sm">
<Calendar />
</Icon>
{formatDate(date, 'PP', locale)}
</Button>
<Popover
side="bottom"
align="start"
sideOffset={8}
className="min-w-[340px] bg-surface-overlay border border-edge-muted rounded-lg shadow-lg p-4"
>
<ZenCalendar value={date} onChange={handleChange} />
</Popover>
</DialogTrigger>
</Column>
);
}
export function AnnotationEditForm({
annotation,
websiteId,
@@ -127,11 +76,16 @@ export function AnnotationEditForm({
return (
<Column gap>
<AnnotationDatePicker
<DatePicker
triggerProps={{ style: { width: '100%', maxWidth: 280 } }}
label={t(labels.date)}
date={date}
value={date}
locale={locale}
onChange={setDate}
onChange={value => {
if (value) {
setDate(value);
}
}}
/>
<Column gap="1">
@@ -13,7 +13,6 @@ import { formatAnnotationDate } from '@/components/charts/ChartAnnotationMarkers
import { Empty } from '@/components/common/Empty';
import { IconLabel } from '@/components/common/IconLabel';
import { Pager } from '@/components/common/Pager';
import { FilterButtons } from '@/components/input/FilterButtons';
import {
useLocale,
useMessages,
@@ -24,6 +23,7 @@ import {
useWebsiteAnnotationsQuery,
} from '@/components/hooks';
import { Edit, Plus } from '@/components/icons';
import { FilterButtons } from '@/components/input/FilterButtons';
import { type AnnotationRange, getAnnotationDateRangeValue } from '@/lib/annotations';
import { AnnotationDeleteButton } from './AnnotationDeleteButton';
import { type Annotation, AnnotationEditForm } from './AnnotationEditForm';
@@ -99,7 +99,7 @@ export function AnnotationsModal({
const rows: Annotation[] = data?.data || [];
return (
<Column gap>
<Column gap flexGrow={1}>
<Row alignItems="center" justifyContent="space-between">
<Heading size="xl">{t(labels.notes)}</Heading>
{!share && (
@@ -155,7 +155,7 @@ export function AnnotationsModal({
<Pager page={page} pageSize={PAGE_SIZE} count={data.count} onPageChange={setPage} />
)}
{onClose && (
<Row justifyContent="flex-end">
<Row justifyContent="flex-end" style={{ marginTop: 'auto' }}>
<Button onPress={onClose}>{t(labels.close)}</Button>
</Row>
)}
+19 -1
View File
@@ -5,7 +5,7 @@ import ChartJS, {
type LegendItem,
type UpdateMode,
} from 'chart.js/auto';
import { useEffect, useMemo, useRef, useState } from 'react';
import { type ReactNode, useEffect, useMemo, useRef, useState } from 'react';
import {
type AnnotationMarker,
type ChartAnnotation,
@@ -26,6 +26,7 @@ export interface ChartProps extends BoxProps {
onTooltip?: (model: any) => void;
hiddenLabels?: Set<string>;
onLegendClick?: (label: string, willBeHidden: boolean) => void;
legendActions?: ReactNode;
annotations?: ChartAnnotation[];
onAnnotationClick?: (annotations: ChartAnnotation[]) => void;
onAnnotationMoreClick?: (annotations: ChartAnnotation[]) => void;
@@ -53,6 +54,7 @@ export function Chart({
chartOptions,
hiddenLabels,
onLegendClick,
legendActions,
annotations,
onAnnotationClick,
onAnnotationMoreClick,
@@ -234,7 +236,23 @@ export function Chart({
)}
</div>
</Box>
{legendActions ? (
<div
style={{
display: 'grid',
gridTemplateColumns: '1fr auto 1fr',
alignItems: 'center',
gap: 8,
}}
>
<div style={{ gridColumn: 2 }}>
<Legend items={legendItems} onClick={handleLegendClick} />
</div>
<div style={{ justifySelf: 'end' }}>{legendActions}</div>
</div>
) : (
<Legend items={legendItems} onClick={handleLegendClick} />
)}
</Column>
);
}