mirror of
https://github.com/umami-software/umami.git
synced 2026-02-12 00:27:11 +01:00
commit
d7141e626e
26 changed files with 52 additions and 9 deletions
|
|
@ -168,7 +168,7 @@ const config = {
|
||||||
destination: '/api/scripts/telemetry',
|
destination: '/api/scripts/telemetry',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: '/teams/:teamId/:path*',
|
source: '/teams/:teamId/:path((?!settings).*)*',
|
||||||
destination: '/:path*',
|
destination: '/:path*',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "2.12.0",
|
"version": "2.12.1",
|
||||||
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
||||||
"author": "Umami Software, Inc. <hello@umami.is>",
|
"author": "Umami Software, Inc. <hello@umami.is>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-weight: 500;
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useContext, useMemo, useState } from 'react';
|
import { useContext, useMemo, useState } from 'react';
|
||||||
import { TooltipPopup } from 'react-basics';
|
import { TextOverflow, TooltipPopup } from 'react-basics';
|
||||||
import { firstBy } from 'thenby';
|
import { firstBy } from 'thenby';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useEscapeKey, useMessages } from 'components/hooks';
|
import { useEscapeKey, useMessages } from 'components/hooks';
|
||||||
|
|
@ -191,7 +191,9 @@ export default function JourneyView() {
|
||||||
})}
|
})}
|
||||||
onClick={() => handleClick(name, columnIndex, paths)}
|
onClick={() => handleClick(name, columnIndex, paths)}
|
||||||
>
|
>
|
||||||
<div className={styles.name}>{name}</div>
|
<div className={styles.name} title={name}>
|
||||||
|
<TextOverflow> {name}</TextOverflow>
|
||||||
|
</div>
|
||||||
<TooltipPopup label={dropOffPercent} disabled={!selected}>
|
<TooltipPopup label={dropOffPercent} disabled={!selected}>
|
||||||
<div className={styles.count} title={nodeCount}>
|
<div className={styles.count} title={nodeCount}>
|
||||||
{formatLongNumber(nodeCount)}
|
{formatLongNumber(nodeCount)}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
import TeamProvider from './TeamProvider';
|
import TeamProvider from './TeamProvider';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
import TeamSettingsLayout from './settings/TeamSettingsLayout';
|
||||||
|
|
||||||
export default function ({ children, params: { teamId } }) {
|
export default function ({ children, params: { teamId } }) {
|
||||||
return <TeamProvider teamId={teamId}>{children}</TeamProvider>;
|
return (
|
||||||
|
<TeamProvider teamId={teamId}>
|
||||||
|
<TeamSettingsLayout>{children}</TeamSettingsLayout>
|
||||||
|
</TeamProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
'use client';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { useMessages, useTeamUrl } from 'components/hooks';
|
||||||
|
import MenuLayout from 'components/layout/MenuLayout';
|
||||||
|
|
||||||
|
export default function TeamSettingsLayout({ children }: { children: ReactNode }) {
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { teamId } = useTeamUrl();
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
key: 'team',
|
||||||
|
label: formatMessage(labels.team),
|
||||||
|
url: `/teams/${teamId}/settings/team`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'websites',
|
||||||
|
label: formatMessage(labels.websites),
|
||||||
|
url: `/teams/${teamId}/settings/websites`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'members',
|
||||||
|
label: formatMessage(labels.members),
|
||||||
|
url: `/teams/${teamId}/settings/members`,
|
||||||
|
},
|
||||||
|
].filter(n => n);
|
||||||
|
|
||||||
|
return <MenuLayout items={items}>{children}</MenuLayout>;
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import PageHeader from 'components/layout/PageHeader';
|
||||||
import { ROLES } from 'lib/constants';
|
import { ROLES } from 'lib/constants';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { Flexbox, Item, Tabs } from 'react-basics';
|
import { Flexbox, Item, Tabs } from 'react-basics';
|
||||||
import TeamLeaveButton from '../../TeamLeaveButton';
|
import TeamLeaveButton from 'app/(main)/settings/teams/TeamLeaveButton';
|
||||||
import TeamManage from './TeamManage';
|
import TeamManage from './TeamManage';
|
||||||
import TeamEditForm from './TeamEditForm';
|
import TeamEditForm from './TeamEditForm';
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from 'react-basics';
|
} from 'react-basics';
|
||||||
import { getRandomChars } from 'next-basics';
|
import { getRandomChars } from 'next-basics';
|
||||||
import { useContext, useRef, useState } from 'react';
|
import { useContext, useRef, useState } from 'react';
|
||||||
import { useApi, useMessages } from 'components/hooks';
|
import { useApi, useMessages, useModified } from 'components/hooks';
|
||||||
import { TeamContext } from 'app/(main)/teams/[teamId]/TeamProvider';
|
import { TeamContext } from 'app/(main)/teams/[teamId]/TeamProvider';
|
||||||
|
|
||||||
const generateId = () => getRandomChars(16);
|
const generateId = () => getRandomChars(16);
|
||||||
|
|
@ -26,12 +26,14 @@ export function TeamEditForm({ teamId, allowEdit }: { teamId: string; allowEdit?
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const [accessCode, setAccessCode] = useState(team.accessCode);
|
const [accessCode, setAccessCode] = useState(team.accessCode);
|
||||||
const { showToast } = useToasts();
|
const { showToast } = useToasts();
|
||||||
|
const { touch } = useModified();
|
||||||
const cloudMode = !!process.env.cloudMode;
|
const cloudMode = !!process.env.cloudMode;
|
||||||
|
|
||||||
const handleSubmit = async (data: any) => {
|
const handleSubmit = async (data: any) => {
|
||||||
mutate(data, {
|
mutate(data, {
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
ref.current.reset(data);
|
ref.current.reset(data);
|
||||||
|
touch('teams');
|
||||||
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Page from 'app/(main)/settings/websites/[websiteId]/page';
|
||||||
|
|
||||||
|
export default function ({ params }) {
|
||||||
|
return <Page params={params} />;
|
||||||
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ export const MetricCard = ({
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.card, className, showPrevious && styles.compare)}>
|
<div className={classNames(styles.card, className, showPrevious && styles.compare)}>
|
||||||
{showLabel && <div className={styles.label}>{label}</div>}
|
{showLabel && <div className={styles.label}>{label}</div>}
|
||||||
<animated.div className={styles.value} title={value.toString()}>
|
<animated.div className={styles.value} title={value?.toString()}>
|
||||||
{props?.x?.to(x => formatValue(x))}
|
{props?.x?.to(x => formatValue(x))}
|
||||||
</animated.div>
|
</animated.div>
|
||||||
{showChange && (
|
{showChange && (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue