mirror of
https://github.com/umami-software/umami.git
synced 2026-02-05 13:17:19 +01:00
Updates for cloud mode.
Some checks failed
Node.js CI / build (postgresql, 18.18) (push) Has been cancelled
Some checks failed
Node.js CI / build (postgresql, 18.18) (push) Has been cancelled
This commit is contained in:
parent
dc1736458b
commit
f40e1b44f3
51 changed files with 251 additions and 173 deletions
|
|
@ -1,12 +1,12 @@
|
|||
import { useState, Key, Fragment } from 'react';
|
||||
import { Modal, Select, ListItem, ListSeparator, Dialog, Row } from '@umami/react-zen';
|
||||
import { Modal, Select, ListItem, ListSeparator, Dialog, SelectProps } from '@umami/react-zen';
|
||||
import { endOfYear } from 'date-fns';
|
||||
import { DatePickerForm } from '@/components/metrics/DatePickerForm';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { DateDisplay } from '@/components/common/DateDisplay';
|
||||
import { parseDateRange } from '@/lib/date';
|
||||
|
||||
export interface DateFilterProps {
|
||||
export interface DateFilterProps extends SelectProps {
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
showAllTime?: boolean;
|
||||
|
|
@ -20,6 +20,7 @@ export function DateFilter({
|
|||
showAllTime,
|
||||
renderDate,
|
||||
placement = 'bottom',
|
||||
...props
|
||||
}: DateFilterProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
|
|
@ -99,8 +100,9 @@ export function DateFilter({
|
|||
};
|
||||
|
||||
return (
|
||||
<Row minWidth="200px">
|
||||
<>
|
||||
<Select
|
||||
{...props}
|
||||
value={value}
|
||||
placeholder={formatMessage(labels.selectDate)}
|
||||
onChange={handleChange}
|
||||
|
|
@ -130,6 +132,6 @@ export function DateFilter({
|
|||
</Dialog>
|
||||
</Modal>
|
||||
)}
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export function LookupField({ websiteId, type, value, onChange, ...props }: Look
|
|||
allowsCustomValue
|
||||
renderEmptyState={() =>
|
||||
isLoading ? (
|
||||
<Loading position="center" icon="dots" />
|
||||
<Loading placement="center" icon="dots" />
|
||||
) : (
|
||||
<Empty message={formatMessage(messages.noResultsFound)} />
|
||||
)
|
||||
|
|
|
|||
27
src/components/input/PreferencesButton.tsx
Normal file
27
src/components/input/PreferencesButton.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { Button, Icon, DialogTrigger, Popover, Column, Label } from '@umami/react-zen';
|
||||
import { TimezoneSetting } from '@/app/(main)/settings/preferences/TimezoneSetting';
|
||||
import { DateRangeSetting } from '@/app/(main)/settings/preferences/DateRangeSetting';
|
||||
import { Settings } from '@/components/icons';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
||||
export function PreferencesButton() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button variant="quiet">
|
||||
<Icon>
|
||||
<Settings />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popover placement="bottom end">
|
||||
<Column gap="3">
|
||||
<Label>{formatMessage(labels.timezone)}</Label>
|
||||
<TimezoneSetting />
|
||||
<Label>{formatMessage(labels.defaultDateRange)}</Label>
|
||||
<DateRangeSetting />
|
||||
</Column>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,31 +11,31 @@ import {
|
|||
Text,
|
||||
Row,
|
||||
} from '@umami/react-zen';
|
||||
import { useMessages, useLoginQuery, useNavigation } from '@/components/hooks';
|
||||
import { useMessages, useLoginQuery, useNavigation, useConfig } from '@/components/hooks';
|
||||
import { LogOut, UserCircle, LockKeyhole } from '@/components/icons';
|
||||
|
||||
export function ProfileButton() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { user } = useLoginQuery();
|
||||
const { renderUrl } = useNavigation();
|
||||
const cloudMode = !!process.env.cloudMode;
|
||||
const { cloudUrl } = useConfig();
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: 'profile',
|
||||
id: 'settings',
|
||||
label: formatMessage(labels.profile),
|
||||
path: renderUrl('/settings/profile'),
|
||||
icon: <UserCircle />,
|
||||
},
|
||||
user.isAdmin &&
|
||||
!cloudMode && {
|
||||
!cloudUrl && {
|
||||
id: 'admin',
|
||||
label: formatMessage(labels.admin),
|
||||
path: '/admin',
|
||||
icon: <LockKeyhole />,
|
||||
},
|
||||
{
|
||||
id: 'LogOut',
|
||||
id: 'logout',
|
||||
label: formatMessage(labels.logout),
|
||||
path: '/logout',
|
||||
icon: <LogOut />,
|
||||
|
|
|
|||
|
|
@ -1,27 +1,71 @@
|
|||
import { Button, Icon, DialogTrigger, Popover, Column, Label } from '@umami/react-zen';
|
||||
import { TimezoneSetting } from '@/app/(main)/settings/preferences/TimezoneSetting';
|
||||
import { DateRangeSetting } from '@/app/(main)/settings/preferences/DateRangeSetting';
|
||||
import { Gear } from '@/components/icons';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { Key } from 'react';
|
||||
import {
|
||||
Icon,
|
||||
Button,
|
||||
MenuTrigger,
|
||||
Popover,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuSeparator,
|
||||
MenuSection,
|
||||
Dialog,
|
||||
SubMenuTrigger,
|
||||
} from '@umami/react-zen';
|
||||
import { useMessages, useLoginQuery, useNavigation, useConfig } from '@/components/hooks';
|
||||
import { LogOut, LockKeyhole, Settings, Knobs } from '@/components/icons';
|
||||
import { PreferenceSettings } from '@/app/(main)/settings/preferences/PreferenceSettings';
|
||||
|
||||
export function SettingsButton() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { user } = useLoginQuery();
|
||||
const { router, renderUrl } = useNavigation();
|
||||
const { cloudMode, cloudUrl } = useConfig();
|
||||
|
||||
const handleAction = (id: Key) => {
|
||||
if (id === 'settings') {
|
||||
if (cloudMode) {
|
||||
window.location.href = `${cloudUrl}/dashboard`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
router.push(renderUrl(`/${id}`));
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button variant="quiet">
|
||||
<MenuTrigger>
|
||||
<Button data-test="button-profile" variant="quiet" autoFocus={false}>
|
||||
<Icon>
|
||||
<Gear />
|
||||
<Settings />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popover placement="bottom end">
|
||||
<Column gap="3">
|
||||
<Label>{formatMessage(labels.timezone)}</Label>
|
||||
<TimezoneSetting />
|
||||
<Label>{formatMessage(labels.defaultDateRange)}</Label>
|
||||
<DateRangeSetting />
|
||||
</Column>
|
||||
<Menu autoFocus="last" onAction={handleAction}>
|
||||
<MenuSection title={user.username}>
|
||||
<MenuSeparator />
|
||||
<MenuItem id="settings" icon={<Settings />} label={formatMessage(labels.settings)} />
|
||||
{cloudMode && (
|
||||
<SubMenuTrigger>
|
||||
<MenuItem
|
||||
icon={<Knobs />}
|
||||
label={formatMessage(labels.preferences)}
|
||||
showSubMenuIcon
|
||||
/>
|
||||
<Popover placement="right bottom">
|
||||
<Dialog>
|
||||
<PreferenceSettings />
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</SubMenuTrigger>
|
||||
)}
|
||||
{!cloudMode && user.isAdmin && (
|
||||
<MenuItem id="admin" icon={<LockKeyhole />} label={formatMessage(labels.admin)} />
|
||||
)}
|
||||
<MenuSeparator />
|
||||
<MenuItem id="logout" icon={<LogOut />} label={formatMessage(labels.logout)} />
|
||||
</MenuSection>
|
||||
</Menu>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
</MenuTrigger>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { Key } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import {
|
||||
Text,
|
||||
Icon,
|
||||
|
|
@ -10,8 +8,8 @@ import {
|
|||
MenuSeparator,
|
||||
Popover,
|
||||
Row,
|
||||
Box,
|
||||
Button,
|
||||
Column,
|
||||
Pressable,
|
||||
Loading,
|
||||
} from '@umami/react-zen';
|
||||
import { useLoginQuery, useMessages, useUserTeamsQuery, useNavigation } from '@/components/hooks';
|
||||
|
|
@ -32,7 +30,7 @@ export function TeamsButton({ showText = true, onAction }: TeamsButtonProps) {
|
|||
const label = teamId ? team?.name : user.username;
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading icon="dots" position="center" />;
|
||||
return <Loading icon="dots" size="sm" placement="center" />;
|
||||
}
|
||||
|
||||
if (!data?.count) {
|
||||
|
|
@ -41,8 +39,15 @@ export function TeamsButton({ showText = true, onAction }: TeamsButtonProps) {
|
|||
|
||||
return (
|
||||
<MenuTrigger>
|
||||
<Button variant="outline">
|
||||
<Row alignItems="center" justifyContent="space-between" flexGrow={1}>
|
||||
<Pressable>
|
||||
<Row
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
flexGrow={1}
|
||||
padding
|
||||
backgroundColor="2"
|
||||
style={{ cursor: 'pointer', textWrap: 'nowrap', outline: 'none' }}
|
||||
>
|
||||
<Row alignItems="center" gap>
|
||||
<Icon>{teamId ? <Users /> : <User />}</Icon>
|
||||
{showText && <Text>{label}</Text>}
|
||||
|
|
@ -53,9 +58,9 @@ export function TeamsButton({ showText = true, onAction }: TeamsButtonProps) {
|
|||
</Icon>
|
||||
)}
|
||||
</Row>
|
||||
</Button>
|
||||
</Pressable>
|
||||
<Popover placement="bottom start">
|
||||
<Box minWidth="300px">
|
||||
<Column minWidth="300px">
|
||||
<Menu
|
||||
selectionMode="single"
|
||||
selectedKeys={selectedKeys}
|
||||
|
|
@ -86,7 +91,7 @@ export function TeamsButton({ showText = true, onAction }: TeamsButtonProps) {
|
|||
))}
|
||||
</MenuSection>
|
||||
</Menu>
|
||||
</Box>
|
||||
</Column>
|
||||
</Popover>
|
||||
</MenuTrigger>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -59,12 +59,14 @@ export function WebsiteDateFilter({
|
|||
</Button>
|
||||
</Row>
|
||||
)}
|
||||
<DateFilter
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
showAllTime={showAllTime}
|
||||
renderDate={+offset !== 0}
|
||||
/>
|
||||
<Row width="200px">
|
||||
<DateFilter
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
showAllTime={showAllTime}
|
||||
renderDate={+offset !== 0}
|
||||
/>
|
||||
</Row>
|
||||
{allowCompare && !isAllTime && (
|
||||
<Row alignItems="center" gap>
|
||||
<Text weight="bold">VS</Text>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue