mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 05:37:20 +01:00
Updated tables. Added MenuButton.
This commit is contained in:
parent
92b283486e
commit
a15c7cd596
27 changed files with 334 additions and 207 deletions
|
|
@ -49,10 +49,11 @@ export function BarChart({
|
|||
const [tooltip, setTooltip] = useState(null);
|
||||
const { theme } = useTheme();
|
||||
const { locale } = useLocale();
|
||||
const { colors } = getThemeColors(theme);
|
||||
const { colors } = useMemo(() => getThemeColors(theme), [theme]);
|
||||
|
||||
const options: any = useMemo(() => {
|
||||
return {
|
||||
__id: new Date().getTime(),
|
||||
scales: {
|
||||
x: {
|
||||
type: XAxisType,
|
||||
|
|
@ -98,21 +99,21 @@ export function BarChart({
|
|||
const handleTooltip = ({ tooltip }: { tooltip: any }) => {
|
||||
const { opacity, labelColors, dataPoints } = tooltip;
|
||||
|
||||
if (opacity) {
|
||||
setTooltip({
|
||||
title: formatDate(
|
||||
new Date(dataPoints[0].raw?.d || dataPoints[0].raw?.x || dataPoints[0].raw),
|
||||
dateFormats[unit],
|
||||
locale,
|
||||
),
|
||||
color: labelColors?.[0]?.backgroundColor,
|
||||
value: currency
|
||||
? formatLongCurrency(dataPoints[0].raw.y, currency)
|
||||
: `${formatLongNumber(dataPoints[0].raw.y)} ${dataPoints[0].dataset.label}`,
|
||||
});
|
||||
} else {
|
||||
setTooltip(null);
|
||||
}
|
||||
setTooltip(
|
||||
opacity
|
||||
? {
|
||||
title: formatDate(
|
||||
new Date(dataPoints[0].raw?.d || dataPoints[0].raw?.x || dataPoints[0].raw),
|
||||
dateFormats[unit],
|
||||
locale,
|
||||
),
|
||||
color: labelColors?.[0]?.backgroundColor,
|
||||
value: currency
|
||||
? formatLongCurrency(dataPoints[0].raw.y, currency)
|
||||
: `${formatLongNumber(dataPoints[0].raw.y)} ${dataPoints[0].dataset.label}`,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
import { Text, List, ListItem } from '@umami/react-zen';
|
||||
import { ReactNode } from 'react';
|
||||
import { Text, List, ListItem, Icon, Row } from '@umami/react-zen';
|
||||
|
||||
export interface SideMenuProps {
|
||||
items: { id: string; label: string; url: string }[];
|
||||
items: { id: string; label: string; url: string; icon?: ReactNode }[];
|
||||
selectedKey?: string;
|
||||
}
|
||||
|
||||
export function SideMenu({ items, selectedKey }: SideMenuProps) {
|
||||
return (
|
||||
<List>
|
||||
{items.map(({ id, label, url }) => {
|
||||
{items.map(({ id, label, url, icon }) => {
|
||||
return (
|
||||
<ListItem key={id} id={id} href={url}>
|
||||
<Text weight={id === selectedKey ? 'bold' : 'regular'}>{label}</Text>
|
||||
<Row alignItems="center" gap>
|
||||
{icon && <Icon>{icon}</Icon>}
|
||||
<Text weight={id === selectedKey ? 'bold' : 'regular'}>{label}</Text>
|
||||
</Row>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ export function useNavigation() {
|
|||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const params = useSearchParams();
|
||||
const [, teamId] = pathname.match(/^\/teams\/([a-f0-9-]+)/) || [];
|
||||
const [, teamId] = pathname.match(/\/teams\/([a-f0-9-]+)/) || [];
|
||||
const [, websiteId] = pathname.match(/\/websites\/([a-f0-9-]+)/) || [];
|
||||
|
||||
const query = useMemo<{ [key: string]: any }>(() => {
|
||||
const obj = {};
|
||||
|
|
@ -26,5 +27,5 @@ export function useNavigation() {
|
|||
return teamId ? `/teams/${teamId}${url}` : url;
|
||||
}
|
||||
|
||||
return { pathname, query, router, renderUrl, renderTeamUrl, teamId };
|
||||
return { pathname, query, router, renderUrl, renderTeamUrl, teamId, websiteId };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ export function DateFilter({
|
|||
value,
|
||||
onChange,
|
||||
showAllTime = false,
|
||||
...props
|
||||
}: DateFilterProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
|
|
@ -93,7 +92,7 @@ export function DateFilter({
|
|||
};
|
||||
|
||||
const renderValue = ({ defaultChildren }) => {
|
||||
return value.startsWith('range') ? (
|
||||
return value?.startsWith('range') ? (
|
||||
<DateDisplay startDate={startDate} endDate={endDate} />
|
||||
) : (
|
||||
defaultChildren
|
||||
|
|
@ -103,10 +102,9 @@ export function DateFilter({
|
|||
return (
|
||||
<>
|
||||
<Select
|
||||
{...props}
|
||||
selectedKey={value}
|
||||
placeholder={formatMessage(labels.selectDate)}
|
||||
onSelectionChange={handleChange}
|
||||
onChange={handleChange}
|
||||
renderValue={renderValue}
|
||||
>
|
||||
{options.map(({ label, value, divider }: any) => {
|
||||
|
|
|
|||
30
src/components/input/MenuButton.tsx
Normal file
30
src/components/input/MenuButton.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { ReactNode, Key } from 'react';
|
||||
import { DialogTrigger, Button, Menu, Popover, Icon } from '@umami/react-zen';
|
||||
import { Lucide } from '@/components/icons';
|
||||
|
||||
export function MenuButton({
|
||||
children,
|
||||
onAction,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
onAction?: (action: string) => void;
|
||||
}) {
|
||||
const handleAction = (key: Key) => {
|
||||
onAction?.(key.toString());
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button variant="outline">
|
||||
<Icon>
|
||||
<Lucide.Ellipsis />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popover>
|
||||
<Menu onAction={handleAction} style={{ minWidth: '140px' }}>
|
||||
{children}
|
||||
</Menu>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
);
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ export function TeamsButton({
|
|||
<Button className={className} variant="quiet">
|
||||
<Row alignItems="center" gap="3">
|
||||
<Icon>{teamId ? <Users /> : <User />}</Icon>
|
||||
{showText && <Text weight="bold">{teamId ? team?.name : user.username}</Text>}
|
||||
{showText && <Text>{teamId ? team?.name : user.username}</Text>}
|
||||
<Icon rotate={90} size="xs">
|
||||
<Icons.Chevron />
|
||||
</Icon>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
import { useState, Key } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Select, ListItem } from '@umami/react-zen';
|
||||
import { useWebsites, useMessages } from '@/components/hooks';
|
||||
import type { SelectProps } from '@umami/react-zen/Select';
|
||||
|
||||
export function WebsiteSelect({
|
||||
websiteId,
|
||||
teamId,
|
||||
variant,
|
||||
onSelect,
|
||||
...props
|
||||
}: {
|
||||
websiteId?: string;
|
||||
teamId?: string;
|
||||
variant?: 'primary' | 'secondary' | 'outline' | 'quiet' | 'danger' | 'zero';
|
||||
onSelect?: (key: any) => void;
|
||||
}) {
|
||||
} & SelectProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedId, setSelectedId] = useState<Key>(websiteId);
|
||||
const [selectedId, setSelectedId] = useState(websiteId);
|
||||
|
||||
const queryResult = useWebsites({ teamId }, { search, pageSize: 5 });
|
||||
|
||||
|
|
@ -26,15 +30,20 @@ export function WebsiteSelect({
|
|||
setSearch(value);
|
||||
};
|
||||
|
||||
const items = queryResult?.result?.data as any[];
|
||||
|
||||
return (
|
||||
<Select
|
||||
items={queryResult?.result?.data as any[]}
|
||||
value={selectedId as string}
|
||||
onChange={handleSelect}
|
||||
{...props}
|
||||
items={items}
|
||||
value={selectedId}
|
||||
placeholder={formatMessage(labels.selectWebsite)}
|
||||
allowSearch={true}
|
||||
onSearch={handleSearch}
|
||||
isLoading={queryResult.query.isLoading}
|
||||
buttonProps={{ variant }}
|
||||
allowSearch={true}
|
||||
searchValue={search}
|
||||
onSearch={handleSearch}
|
||||
onChange={handleSelect}
|
||||
>
|
||||
{({ id, name }: any) => <ListItem key={id}>{name}</ListItem>}
|
||||
</Select>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTheme } from '@umami/react-zen';
|
||||
import { BarChart, BarChartProps } from '@/components/charts/BarChart';
|
||||
import { useLocale, useMessages } from '@/components/hooks';
|
||||
|
|
@ -28,8 +28,8 @@ export function PageviewsChart({
|
|||
}: PageviewsChartProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { theme } = useTheme();
|
||||
const { colors } = getThemeColors(theme);
|
||||
const { locale } = useLocale();
|
||||
const { colors } = useMemo(() => getThemeColors(theme), [theme]);
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (!data) {
|
||||
|
|
@ -37,6 +37,7 @@ export function PageviewsChart({
|
|||
}
|
||||
|
||||
return {
|
||||
__id: new Date().getTime(),
|
||||
datasets: [
|
||||
{
|
||||
label: formatMessage(labels.visitors),
|
||||
|
|
@ -78,6 +79,8 @@ export function PageviewsChart({
|
|||
};
|
||||
}, [data, locale]);
|
||||
|
||||
const renderXLabel = useCallback(renderDateLabels(unit, locale), [unit, locale]);
|
||||
|
||||
return (
|
||||
<BarChart
|
||||
{...props}
|
||||
|
|
@ -85,7 +88,7 @@ export function PageviewsChart({
|
|||
unit={unit}
|
||||
isLoading={isLoading}
|
||||
isAllTime={isAllTime}
|
||||
renderXLabel={renderDateLabels(unit, locale)}
|
||||
renderXLabel={renderXLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue