mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 13:47:15 +01:00
Merge branch 'dev' into localization
This commit is contained in:
commit
f0c6960dc3
72 changed files with 3119 additions and 515 deletions
10
src/components/common/Breadcrumb.module.css
Normal file
10
src/components/common/Breadcrumb.module.css
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.bar {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
color: var(--base600);
|
||||
}
|
||||
|
||||
.link span {
|
||||
color: var(--base700) !important;
|
||||
}
|
||||
37
src/components/common/Breadcrumb.tsx
Normal file
37
src/components/common/Breadcrumb.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import Link from 'next/link';
|
||||
import { Flexbox, Icon, Icons, Text } from 'react-basics';
|
||||
import styles from './Breadcrumb.module.css';
|
||||
|
||||
export interface BreadcrumbProps {
|
||||
data: {
|
||||
url?: string;
|
||||
label: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export function Breadcrumb({ data }: BreadcrumbProps) {
|
||||
return (
|
||||
<Flexbox alignItems="center" gap={3} className={styles.bar}>
|
||||
{data.map((a, i) => {
|
||||
return (
|
||||
<>
|
||||
{a.url ? (
|
||||
<Link href={a.url} className={styles.link}>
|
||||
<Text>{a.label}</Text>
|
||||
</Link>
|
||||
) : (
|
||||
<Text>{a.label}</Text>
|
||||
)}
|
||||
{i !== data.length - 1 ? (
|
||||
<Icon rotate={270}>
|
||||
<Icons.ChevronDown />
|
||||
</Icon>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</Flexbox>
|
||||
);
|
||||
}
|
||||
|
||||
export default Breadcrumb;
|
||||
|
|
@ -11,5 +11,5 @@
|
|||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,5 +35,5 @@ a.item.selected,
|
|||
|
||||
.submenu a.item {
|
||||
color: var(--base600);
|
||||
margin-left: 40px;
|
||||
margin-inline-start: 40px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
z-index: var(--z-index-popup);
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--border-color);
|
||||
margin-left: 10px;
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
|
||||
.item {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
font-size: 11px;
|
||||
color: var(--base600);
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
|
||||
.name {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function ProfileButton() {
|
|||
|
||||
return (
|
||||
<PopupTrigger>
|
||||
<Button variant="quiet">
|
||||
<Button data-test="button-profile" variant="quiet">
|
||||
<Icon>
|
||||
<Icons.Profile />
|
||||
</Icon>
|
||||
|
|
@ -41,7 +41,7 @@ export function ProfileButton() {
|
|||
<Text>{formatMessage(labels.profile)}</Text>
|
||||
</Item>
|
||||
{!cloudMode && (
|
||||
<Item key="logout" className={styles.item}>
|
||||
<Item data-test="item-logout" key="logout" className={styles.item}>
|
||||
<Icon>
|
||||
<Icons.Logout />
|
||||
</Icon>
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@
|
|||
}
|
||||
|
||||
.buttons button:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-start-end-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
border-inline-end: 1px solid var(--base400);
|
||||
}
|
||||
|
||||
.buttons button:last-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 1px solid var(--base400) !important;
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useDateRange } from 'components/hooks';
|
||||
import { useDateRange, useLocale } from 'components/hooks';
|
||||
import { isAfter } from 'date-fns';
|
||||
import { getOffsetDateRange } from 'lib/date';
|
||||
import { Button, Icon, Icons } from 'react-basics';
|
||||
|
|
@ -7,6 +7,7 @@ import styles from './WebsiteDateFilter.module.css';
|
|||
import { DateRange } from 'lib/types';
|
||||
|
||||
export function WebsiteDateFilter({ websiteId }: { websiteId: string }) {
|
||||
const { dir } = useLocale();
|
||||
const [dateRange, setDateRange] = useDateRange(websiteId);
|
||||
const { value, startDate, endDate, offset } = dateRange;
|
||||
const disableForward =
|
||||
|
|
@ -25,12 +26,12 @@ export function WebsiteDateFilter({ websiteId }: { websiteId: string }) {
|
|||
{value !== 'all' && (
|
||||
<div className={styles.buttons}>
|
||||
<Button onClick={() => handleIncrement(-1)}>
|
||||
<Icon rotate={90}>
|
||||
<Icon rotate={dir === 'rtl' ? 270 : 90}>
|
||||
<Icons.ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Button onClick={() => handleIncrement(1)} disabled={disableForward}>
|
||||
<Icon rotate={270}>
|
||||
<Icon rotate={dir === 'rtl' ? 90 : 270}>
|
||||
<Icons.ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
.menu {
|
||||
width: 240px;
|
||||
padding-top: 34px;
|
||||
padding-right: 20px;
|
||||
padding-inline-end: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border-right: 2px solid var(--base200);
|
||||
border-inline-end: 2px solid var(--base200);
|
||||
padding: 1rem 2rem;
|
||||
gap: var(--size500);
|
||||
font-weight: 600;
|
||||
width: 200px;
|
||||
margin-right: -2px;
|
||||
margin-inline-end: -2px;
|
||||
}
|
||||
|
||||
a.item {
|
||||
|
|
@ -43,7 +43,7 @@ a.item {
|
|||
|
||||
.item.selected {
|
||||
color: var(--base900);
|
||||
border-right-color: var(--primary400);
|
||||
border-inline-end-color: var(--primary400);
|
||||
background: var(--blue100);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,13 @@
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--base700);
|
||||
margin-right: 1rem;
|
||||
margin-inline-end: 1rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
|
|
|||
|
|
@ -7,23 +7,29 @@ export function PageHeader({
|
|||
title,
|
||||
icon,
|
||||
className,
|
||||
breadcrumb,
|
||||
children,
|
||||
}: {
|
||||
title?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
className?: string;
|
||||
breadcrumb?: ReactNode;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className={classNames(styles.header, className)}>
|
||||
{icon && (
|
||||
<Icon size="lg" className={styles.icon}>
|
||||
{icon}
|
||||
</Icon>
|
||||
)}
|
||||
{title && <div className={styles.title}>{title}</div>}
|
||||
<div className={styles.actions}>{children}</div>
|
||||
</div>
|
||||
<>
|
||||
<div className={styles.breadcrumb}>{breadcrumb}</div>
|
||||
<div className={classNames(styles.header, className)}>
|
||||
{icon && (
|
||||
<Icon size="lg" className={styles.icon}>
|
||||
{icon}
|
||||
</Icon>
|
||||
)}
|
||||
|
||||
{title && <div className={styles.title}>{title}</div>}
|
||||
<div className={styles.actions}>{children}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 20px;
|
||||
margin-inline-start: 20px;
|
||||
}
|
||||
|
||||
.text {
|
||||
|
|
@ -13,5 +13,5 @@
|
|||
|
||||
.value {
|
||||
font-weight: 600;
|
||||
margin-right: 4px;
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
}
|
||||
|
||||
.calendars > div + div {
|
||||
margin-left: 20px;
|
||||
padding-left: 20px;
|
||||
border-left: 1px solid var(--base300);
|
||||
margin-inline-start: 20px;
|
||||
padding-inline-start: 20px;
|
||||
border-inline-start: 1px solid var(--base300);
|
||||
}
|
||||
|
||||
.filter {
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
.calendars > div + div {
|
||||
padding: 0;
|
||||
margin-left: 0;
|
||||
margin-inline-start: 0;
|
||||
margin-top: 20px;
|
||||
border: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@
|
|||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.row:hover {
|
||||
background-color: var(--base75);
|
||||
}
|
||||
|
||||
.label {
|
||||
|
|
@ -46,6 +51,7 @@
|
|||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
flex: 2;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.label a {
|
||||
|
|
@ -76,7 +82,7 @@
|
|||
position: relative;
|
||||
width: 50px;
|
||||
color: var(--base600);
|
||||
border-left: 1px solid var(--base600);
|
||||
border-inline-start: 1px solid var(--base600);
|
||||
padding-inline-start: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ export function MetricsTable({
|
|||
country,
|
||||
region,
|
||||
city,
|
||||
limit,
|
||||
search,
|
||||
},
|
||||
{ retryDelay: delay || DEFAULT_ANIMATION_DURATION, onDataLoad },
|
||||
);
|
||||
|
|
@ -86,20 +88,8 @@ export function MetricsTable({
|
|||
}
|
||||
}
|
||||
|
||||
if (search) {
|
||||
items = items.filter(({ x, ...data }) => {
|
||||
const value = formatValue(x, type, data);
|
||||
|
||||
return value?.toLowerCase().includes(search.toLowerCase());
|
||||
});
|
||||
}
|
||||
|
||||
items = percentFilter(items);
|
||||
|
||||
if (limit) {
|
||||
items = items.slice(0, limit - 1);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
return [];
|
||||
|
|
@ -114,6 +104,7 @@ export function MetricsTable({
|
|||
className={styles.search}
|
||||
value={search}
|
||||
onSearch={setSearch}
|
||||
delay={300}
|
||||
autoFocus={true}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue