mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Redesigned filter bar.
This commit is contained in:
parent
76cab03bb2
commit
6589bc6ecb
@ -9,6 +9,7 @@ import WebsiteExpandedView from './WebsiteExpandedView';
|
||||
import WebsiteHeader from './WebsiteHeader';
|
||||
import WebsiteMetricsBar from './WebsiteMetricsBar';
|
||||
import WebsiteTableView from './WebsiteTableView';
|
||||
import { FILTER_COLUMNS } from 'lib/constants';
|
||||
|
||||
export default function WebsiteDetails({ websiteId }: { websiteId: string }) {
|
||||
const { data: website, isLoading, error } = useWebsite(websiteId);
|
||||
@ -20,13 +21,20 @@ export default function WebsiteDetails({ websiteId }: { websiteId: string }) {
|
||||
}
|
||||
|
||||
const showLinks = !pathname.includes('/share/');
|
||||
const { view, ...params } = query;
|
||||
const { view } = query;
|
||||
|
||||
const params = Object.keys(query).reduce((obj, key) => {
|
||||
if (FILTER_COLUMNS[key]) {
|
||||
obj[key] = query[key];
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebsiteHeader websiteId={websiteId} showLinks={showLinks} />
|
||||
<FilterTags websiteId={websiteId} params={params} />
|
||||
<WebsiteMetricsBar websiteId={websiteId} sticky={true} />
|
||||
<WebsiteMetricsBar websiteId={websiteId} />
|
||||
<WebsiteChart websiteId={websiteId} />
|
||||
{!website && <Loading icon="dots" style={{ minHeight: 300 }} />}
|
||||
{website && (
|
||||
|
@ -9,9 +9,15 @@ import styles from './WebsiteFilterButton.module.css';
|
||||
export function WebsiteFilterButton({
|
||||
websiteId,
|
||||
className,
|
||||
position = 'bottom',
|
||||
alignment = 'end',
|
||||
showText = true,
|
||||
}: {
|
||||
websiteId: string;
|
||||
className?: string;
|
||||
position?: 'bottom' | 'top' | 'left' | 'right';
|
||||
alignment?: 'end' | 'center' | 'start';
|
||||
showText?: boolean;
|
||||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { renderUrl, router } = useNavigation();
|
||||
@ -30,9 +36,9 @@ export function WebsiteFilterButton({
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.filter)}</Text>
|
||||
{showText && <Text>{formatMessage(labels.filter)}</Text>}
|
||||
</Button>
|
||||
<Popup position="bottom" alignment="end">
|
||||
<Popup position={position} alignment={alignment}>
|
||||
{(close: () => void) => {
|
||||
return (
|
||||
<PopupForm>
|
||||
|
@ -30,6 +30,11 @@ export function WebsiteHeader({
|
||||
icon: <Icons.Overview />,
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
label: formatMessage(labels.compare),
|
||||
icon: <Icons.Compare />,
|
||||
path: '/compare',
|
||||
},
|
||||
{
|
||||
label: formatMessage(labels.realtime),
|
||||
icon: <Icons.Clock />,
|
||||
|
@ -38,9 +38,3 @@
|
||||
border-bottom: 1px solid var(--base300);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -5,18 +5,10 @@ import MetricCard from 'components/metrics/MetricCard';
|
||||
import MetricsBar from 'components/metrics/MetricsBar';
|
||||
import { formatShortTime } from 'lib/format';
|
||||
import WebsiteFilterButton from './WebsiteFilterButton';
|
||||
import styles from './WebsiteMetricsBar.module.css';
|
||||
import useWebsiteStats from 'components/hooks/queries/useWebsiteStats';
|
||||
import styles from './WebsiteMetricsBar.module.css';
|
||||
|
||||
export function WebsiteMetricsBar({
|
||||
websiteId,
|
||||
showFilter = true,
|
||||
sticky,
|
||||
}: {
|
||||
websiteId: string;
|
||||
showFilter?: boolean;
|
||||
sticky?: boolean;
|
||||
}) {
|
||||
export function WebsiteMetricsBar({ websiteId, sticky }: { websiteId: string; sticky?: boolean }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { ref, isSticky } = useSticky({ enabled: sticky });
|
||||
const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId);
|
||||
@ -89,7 +81,7 @@ export function WebsiteMetricsBar({
|
||||
)}
|
||||
</MetricsBar>
|
||||
<div className={styles.actions}>
|
||||
{showFilter && <WebsiteFilterButton websiteId={websiteId} className={styles.button} />}
|
||||
<WebsiteFilterButton websiteId={websiteId} />
|
||||
<WebsiteDateFilter websiteId={websiteId} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,13 @@
|
||||
import WebsiteHeader from '../WebsiteHeader';
|
||||
import WebsiteMetricsBar from '../WebsiteMetricsBar';
|
||||
|
||||
export function WebsiteComparePage({ websiteId }) {
|
||||
return (
|
||||
<>
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<WebsiteMetricsBar websiteId={websiteId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default WebsiteComparePage;
|
10
src/app/(main)/websites/[websiteId]/compare/page.tsx
Normal file
10
src/app/(main)/websites/[websiteId]/compare/page.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import WebsiteComparePage from './WebsiteComparePage';
|
||||
import { Metadata } from 'next';
|
||||
|
||||
export default function ({ params: { websiteId } }) {
|
||||
return <WebsiteComparePage websiteId={websiteId} />;
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Website Comparison',
|
||||
};
|
1
src/assets/compare.svg
Normal file
1
src/assets/compare.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg height="512" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M6 22a1 1 0 0 1-.71-.29l-4-4a1 1 0 0 1 0-1.42l4-4a1 1 0 0 1 1.42 1.42L4.41 16H22a1 1 0 0 1 0 2H4.41l2.3 2.29a1 1 0 0 1 0 1.42A1 1 0 0 1 6 22zm12-10a1 1 0 0 1-.71-.29 1 1 0 0 1 0-1.42L19.59 8H2a1 1 0 0 1 0-2h17.59l-2.3-2.29a1 1 0 0 1 1.42-1.42l4 4a1 1 0 0 1 0 1.42l-4 4A1 1 0 0 1 18 12z"/></svg>
|
After Width: | Height: | Size: 388 B |
@ -1 +1 @@
|
||||
<svg clip-rule="evenodd" fill-rule="evenodd" height="512" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg"><g id="Icon"><path d="m19.393 10.825c-.097-.403.151-.808.553-.905.402-.098.808.15.905.553.181.75.277 1.533.277 2.338 0 5.485-4.453 9.939-9.939 9.939-5.485 0-9.939-4.454-9.939-9.939 0-5.486 4.454-9.939 9.939-9.939.805 0 1.588.096 2.338.277.403.097.651.503.553.905-.097.402-.502.65-.905.553-.637-.154-1.302-.235-1.986-.235-4.658 0-8.439 3.781-8.439 8.439s3.781 8.439 8.439 8.439 8.439-3.781 8.439-8.439c0-.684-.081-1.349-.235-1.986z"/><path d="m14.764 12.811c0-.414.336-.75.75-.75.413 0 .75.336.75.75 0 2.8-2.274 5.074-5.075 5.074-2.8 0-5.074-2.274-5.074-5.074 0-2.801 2.274-5.075 5.074-5.075.414 0 .75.337.75.75 0 .414-.336.75-.75.75-1.973 0-3.574 1.602-3.574 3.575s1.601 3.574 3.574 3.574 3.575-1.601 3.575-3.574z"/><path d="m22.53 5.588-3.057 3.058c-.141.141-.332.22-.531.22h-3.058c-.414 0-.75-.336-.75-.75v-3.058c0-.199.079-.39.22-.531l3.058-3.057c.184-.184.45-.26.703-.2s.457.246.539.493l.646 1.937 1.937.646c.247.082.433.286.493.539s-.016.519-.2.703zm-1.918-.202-1.142-.381c-.224-.075-.4-.251-.475-.475l-.381-1.142-1.98 1.98v1.998h1.998z"/><path d="m15.354 7.585c.293-.293.768-.293 1.061 0s.293.768 0 1.061l-4.587 4.586c-.293.293-.768.293-1.06 0-.293-.292-.293-.767 0-1.06z"/></g></svg>
|
||||
<svg clip-rule="evenodd" fill-rule="evenodd" height="512" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M19.393 10.825a.75.75 0 0 1 1.458-.352c.181.75.277 1.533.277 2.338 0 5.485-4.453 9.939-9.939 9.939-5.485 0-9.939-4.454-9.939-9.939 0-5.486 4.454-9.939 9.939-9.939.805 0 1.588.096 2.338.277a.75.75 0 1 1-.352 1.458A8.442 8.442 0 0 0 2.75 12.811a8.442 8.442 0 0 0 8.439 8.439 8.442 8.442 0 0 0 8.204-10.425z"/><path d="M14.764 12.811a.75.75 0 0 1 1.5 0c0 2.8-2.274 5.074-5.075 5.074a5.077 5.077 0 0 1-5.074-5.074 5.077 5.077 0 0 1 5.074-5.075.75.75 0 0 1 0 1.5 3.575 3.575 0 1 0 3.575 3.575zm7.766-7.223-3.057 3.058a.75.75 0 0 1-.531.22h-3.058a.75.75 0 0 1-.75-.75V5.058a.75.75 0 0 1 .22-.531l3.058-3.057a.75.75 0 0 1 1.242.293L20.3 3.7l1.937.646a.75.75 0 0 1 .293 1.242zm-1.918-.202-1.142-.381a.753.753 0 0 1-.475-.475l-.381-1.142-1.98 1.98v1.998h1.998z"/><path d="M15.354 7.585a.75.75 0 1 1 1.061 1.061l-4.587 4.586a.749.749 0 1 1-1.06-1.06z"/></svg>
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.0 KiB |
@ -6,6 +6,7 @@ import Bolt from 'assets/bolt.svg';
|
||||
import Calendar from 'assets/calendar.svg';
|
||||
import Change from 'assets/change.svg';
|
||||
import Clock from 'assets/clock.svg';
|
||||
import Compare from 'assets/compare.svg';
|
||||
import Dashboard from 'assets/dashboard.svg';
|
||||
import Eye from 'assets/eye.svg';
|
||||
import Gear from 'assets/gear.svg';
|
||||
@ -32,6 +33,7 @@ const icons = {
|
||||
Calendar,
|
||||
Change,
|
||||
Clock,
|
||||
Compare,
|
||||
Dashboard,
|
||||
Eye,
|
||||
Gear,
|
||||
|
@ -247,6 +247,7 @@ export const labels = defineMessages({
|
||||
id: 'label.journey-description',
|
||||
defaultMessage: 'Understand how users nagivate through your website.',
|
||||
},
|
||||
compare: { id: 'label.compare', defaultMessage: 'Compare' },
|
||||
});
|
||||
|
||||
export const messages = defineMessages({
|
||||
|
@ -2,6 +2,12 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--base75);
|
||||
padding: 10px 20px;
|
||||
border: 1px solid var(--base400);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.label {
|
||||
@ -12,12 +18,13 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--base75);
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
background: var(--base50);
|
||||
border: 1px solid var(--base400);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 1px 1px 1px var(--base500);
|
||||
padding: 8px 16px;
|
||||
padding: 6px 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -27,6 +34,8 @@
|
||||
|
||||
.close {
|
||||
font-weight: 700;
|
||||
align-self: center;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.name,
|
||||
|
@ -13,6 +13,7 @@ import FieldFilterEditForm from 'app/(main)/reports/[reportId]/FieldFilterEditFo
|
||||
import { 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,
|
||||
@ -100,6 +101,7 @@ export function FilterTags({
|
||||
</PopupTrigger>
|
||||
);
|
||||
})}
|
||||
<WebsiteFilterButton websiteId={websiteId} alignment="center" showText={false} />
|
||||
<Button className={styles.close} variant="quiet" onClick={handleResetFilter}>
|
||||
<Icon>
|
||||
<Icons.Close />
|
||||
|
@ -2,10 +2,17 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: 90px;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.card:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.card:last-child {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -27,9 +27,6 @@ export const MetricCard = ({
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.card, className)}>
|
||||
<animated.div className={styles.value} title={props?.x as any}>
|
||||
{props?.x?.to(x => format(x))}
|
||||
</animated.div>
|
||||
<div className={styles.label}>
|
||||
{label}
|
||||
{~~change !== 0 && !hideComparison && (
|
||||
@ -45,6 +42,9 @@ export const MetricCard = ({
|
||||
</animated.span>
|
||||
)}
|
||||
</div>
|
||||
<animated.div className={styles.value} title={props?.x as any}>
|
||||
{props?.x?.to(x => format(x))}
|
||||
</animated.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
1
src/declaration.d.ts
vendored
1
src/declaration.d.ts
vendored
@ -1,4 +1,5 @@
|
||||
declare module 'cors';
|
||||
declare module 'dateformat';
|
||||
declare module 'debug';
|
||||
declare module 'chartjs-adapter-date-fns';
|
||||
declare module 'md5';
|
||||
|
@ -119,10 +119,7 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio
|
||||
};
|
||||
}
|
||||
|
||||
async function rawQuery<T = unknown>(
|
||||
query: string,
|
||||
params: Record<string, unknown> = {},
|
||||
): Promise<T> {
|
||||
async function rawQuery(query: string, params: Record<string, unknown> = {}): Promise<unknown[]> {
|
||||
if (process.env.LOG_QUERY) {
|
||||
log('QUERY:\n', query);
|
||||
log('PARAMETERS:\n', params);
|
||||
|
@ -33,8 +33,8 @@ async function relationalQuery(
|
||||
|
||||
let excludeDomain = '';
|
||||
if (column === 'referrer_domain') {
|
||||
excludeDomain =
|
||||
'and (website_event.referrer_domain != {{websiteDomain}} or website_event.referrer_domain is null)';
|
||||
excludeDomain = `and website_event.referrer_domain != {{websiteDomain}}
|
||||
and website_event.referrer_domain is not null`;
|
||||
}
|
||||
|
||||
return rawQuery(
|
||||
@ -72,7 +72,7 @@ async function clickhouseQuery(
|
||||
|
||||
let excludeDomain = '';
|
||||
if (column === 'referrer_domain') {
|
||||
excludeDomain = 'and referrer_domain != {websiteDomain:String}';
|
||||
excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`;
|
||||
}
|
||||
|
||||
return rawQuery(
|
||||
@ -90,8 +90,8 @@ async function clickhouseQuery(
|
||||
offset ${offset}
|
||||
`,
|
||||
params,
|
||||
).then(a => {
|
||||
return Object.values(a).map(a => {
|
||||
).then((result: any) => {
|
||||
return Object.values(result).map((a: any) => {
|
||||
return { x: a.x, y: Number(a.y) };
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user