mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Refactored teams components.
This commit is contained in:
parent
6253d55790
commit
8b48130d5f
@ -6,7 +6,9 @@ import { useMessages, useLocale } from 'components/hooks';
|
|||||||
import { formatDate } from 'lib/date';
|
import { formatDate } from 'lib/date';
|
||||||
import styles from './RetentionTable.module.css';
|
import styles from './RetentionTable.module.css';
|
||||||
|
|
||||||
export function RetentionTable() {
|
const DAYS = [1, 2, 3, 4, 5, 6, 7, 14, 21, 28];
|
||||||
|
|
||||||
|
export function RetentionTable({ days = DAYS }) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
const { report } = useContext(ReportContext);
|
const { report } = useContext(ReportContext);
|
||||||
@ -16,8 +18,6 @@ export function RetentionTable() {
|
|||||||
return <EmptyPlaceholder />;
|
return <EmptyPlaceholder />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const days = [1, 2, 3, 4, 5, 6, 7, 14, 21, 28];
|
|
||||||
|
|
||||||
const rows = data.reduce((arr, row) => {
|
const rows = data.reduce((arr, row) => {
|
||||||
const { date, visitors, day } = row;
|
const { date, visitors, day } = row;
|
||||||
if (day === 0) {
|
if (day === 0) {
|
||||||
|
@ -9,7 +9,7 @@ export default function SettingsLayout({ children }) {
|
|||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const cloudMode = Boolean(process.env.cloudMode);
|
const cloudMode = !!process.env.cloudMode;
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{ key: 'websites', label: formatMessage(labels.websites), url: '/settings/websites' },
|
{ key: 'websites', label: formatMessage(labels.websites), url: '/settings/websites' },
|
||||||
@ -20,6 +20,10 @@ export default function SettingsLayout({ children }) {
|
|||||||
|
|
||||||
const getKey = () => items.find(({ url }) => pathname === url)?.key;
|
const getKey = () => items.find(({ url }) => pathname === url)?.key;
|
||||||
|
|
||||||
|
if (cloudMode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.layout}>
|
<div className={styles.layout}>
|
||||||
{!cloudMode && (
|
{!cloudMode && (
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import ProfileHeader from './ProfileHeader';
|
import ProfileHeader from './ProfileHeader';
|
||||||
import ProfileSettings from './ProfileSettings';
|
import ProfileSettings from './ProfileSettings';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
@ -9,3 +10,7 @@ export default function () {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Profile Settings | umami',
|
||||||
|
};
|
@ -10,6 +10,7 @@ import {
|
|||||||
} from 'react-basics';
|
} from 'react-basics';
|
||||||
import useApi from 'components/hooks/useApi';
|
import useApi from 'components/hooks/useApi';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
import { setValue } from 'store/cache';
|
||||||
|
|
||||||
export function TeamJoinForm({ onSave, onClose }) {
|
export function TeamJoinForm({ onSave, onClose }) {
|
||||||
const { formatMessage, labels, getMessage } = useMessages();
|
const { formatMessage, labels, getMessage } = useMessages();
|
||||||
@ -20,8 +21,9 @@ export function TeamJoinForm({ onSave, onClose }) {
|
|||||||
const handleSubmit = async data => {
|
const handleSubmit = async data => {
|
||||||
mutate(data, {
|
mutate(data, {
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
onSave();
|
setValue('teams', Date.now());
|
||||||
onClose();
|
onSave?.();
|
||||||
|
onClose?.();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@ export function TeamLeaveButton({ teamId, teamName, onLeave }) {
|
|||||||
<ModalTrigger>
|
<ModalTrigger>
|
||||||
<Button>
|
<Button>
|
||||||
<Icon rotate={dir === 'rtl' ? 180 : 0}>
|
<Icon rotate={dir === 'rtl' ? 180 : 0}>
|
||||||
<Icons.ArrowRight />
|
<Icons.Logout />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text>{formatMessage(labels.leave)}</Text>
|
<Text>{formatMessage(labels.leave)}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -3,10 +3,12 @@ import DataTable from 'components/common/DataTable';
|
|||||||
import TeamsTable from 'app/(main)/settings/teams/TeamsTable';
|
import TeamsTable from 'app/(main)/settings/teams/TeamsTable';
|
||||||
import useApi from 'components/hooks/useApi';
|
import useApi from 'components/hooks/useApi';
|
||||||
import useFilterQuery from 'components/hooks/useFilterQuery';
|
import useFilterQuery from 'components/hooks/useFilterQuery';
|
||||||
|
import useCache from 'store/cache';
|
||||||
|
|
||||||
export function TeamsDataTable() {
|
export function TeamsDataTable() {
|
||||||
const { get } = useApi();
|
const { get } = useApi();
|
||||||
const queryResult = useFilterQuery(['teams'], params => {
|
const modified = useCache(state => state?.websites);
|
||||||
|
const queryResult = useFilterQuery(['teams', { modified }], params => {
|
||||||
return get(`/teams`, {
|
return get(`/teams`, {
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
@ -21,18 +21,18 @@ export function TeamsTable({ data = [] }) {
|
|||||||
{row => {
|
{row => {
|
||||||
const { id, name, teamUser } = row;
|
const { id, name, teamUser } = row;
|
||||||
const owner = teamUser.find(({ role }) => role === ROLES.teamOwner);
|
const owner = teamUser.find(({ role }) => role === ROLES.teamOwner);
|
||||||
const showDelete = user.id === owner?.userId;
|
const isOwner = user.id === owner?.userId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showDelete && <TeamDeleteButton teamId={id} teamName={name} />}
|
{isOwner && <TeamDeleteButton teamId={id} teamName={name} />}
|
||||||
{!showDelete && <TeamLeaveButton teamId={id} teamName={name} />}
|
{!isOwner && <TeamLeaveButton teamId={id} teamName={name} />}
|
||||||
<Link href={`/settings/teams/${id}`}>
|
<Link href={`/settings/teams/${id}`}>
|
||||||
<Button>
|
<Button>
|
||||||
<Icon>
|
<Icon>
|
||||||
<Icons.Edit />
|
<Icons.Edit />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text>{formatMessage(labels.edit)}</Text>
|
<Text>{formatMessage(isOwner ? labels.edit : labels.view)}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
|
@ -3,12 +3,13 @@ import { useState } from 'react';
|
|||||||
import { Button, Form, FormButtons, GridColumn, Loading, SubmitButton, Toggle } from 'react-basics';
|
import { Button, Form, FormButtons, GridColumn, Loading, SubmitButton, Toggle } from 'react-basics';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
import WebsitesDataTable from '../../websites/WebsitesDataTable';
|
import WebsitesDataTable from '../../websites/WebsitesDataTable';
|
||||||
|
import Empty from 'components/common/Empty';
|
||||||
|
|
||||||
export function TeamWebsiteAddForm({ teamId, onSave, onClose }) {
|
export function TeamWebsiteAddForm({ teamId, onSave, onClose }) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { get, post, useQuery, useMutation } = useApi();
|
const { get, post, useQuery, useMutation } = useApi();
|
||||||
const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data));
|
const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data));
|
||||||
const { data: websites } = useQuery(['websites'], () => get('/websites'));
|
const { data: websites, isLoading } = useQuery(['websites'], () => get('/websites'));
|
||||||
const [selected, setSelected] = useState([]);
|
const [selected, setSelected] = useState([]);
|
||||||
const hasData = websites && websites.data.length > 0;
|
const hasData = websites && websites.data.length > 0;
|
||||||
|
|
||||||
@ -30,7 +31,8 @@ export function TeamWebsiteAddForm({ teamId, onSave, onClose }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!hasData && <Loading />}
|
{isLoading && !hasData && <Loading />}
|
||||||
|
{!isLoading && !hasData && <Empty />}
|
||||||
{hasData && (
|
{hasData && (
|
||||||
<Form onSubmit={handleSubmit} error={error}>
|
<Form onSubmit={handleSubmit} error={error}>
|
||||||
<WebsitesDataTable showHeader={false} showActions={false}>
|
<WebsitesDataTable showHeader={false} showActions={false}>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import TeamsDataTable from './TeamsDataTable';
|
import TeamsDataTable from './TeamsDataTable';
|
||||||
import TeamsHeader from './TeamsHeader';
|
import TeamsHeader from './TeamsHeader';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
if (process.env.cloudMode) {
|
if (process.env.cloudMode) {
|
||||||
@ -13,3 +14,7 @@ export default function () {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Teams Settings | umami',
|
||||||
|
};
|
@ -2,10 +2,6 @@ import UsersDataTable from './UsersDataTable';
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
if (process.env.cloudMode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <UsersDataTable />;
|
return <UsersDataTable />;
|
||||||
}
|
}
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable';
|
import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable';
|
||||||
import useUser from 'components/hooks/useUser';
|
import useUser from 'components/hooks/useUser';
|
||||||
import useApi from 'components/hooks/useApi';
|
import useApi from 'components/hooks/useApi';
|
||||||
@ -6,10 +7,20 @@ import DataTable from 'components/common/DataTable';
|
|||||||
import useFilterQuery from 'components/hooks/useFilterQuery';
|
import useFilterQuery from 'components/hooks/useFilterQuery';
|
||||||
import useCache from 'store/cache';
|
import useCache from 'store/cache';
|
||||||
|
|
||||||
|
export interface WebsitesDataTableProps {
|
||||||
|
allowEdit?: boolean;
|
||||||
|
allowView?: boolean;
|
||||||
|
showActions?: boolean;
|
||||||
|
showTeam?: boolean;
|
||||||
|
includeTeams?: boolean;
|
||||||
|
onlyTeams?: boolean;
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
function useWebsites({ includeTeams, onlyTeams }) {
|
function useWebsites({ includeTeams, onlyTeams }) {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { get } = useApi();
|
const { get } = useApi();
|
||||||
const modified = useCache(state => state?.websites);
|
const modified = useCache((state: any) => state?.websites);
|
||||||
|
|
||||||
return useFilterQuery(
|
return useFilterQuery(
|
||||||
['websites', { includeTeams, onlyTeams, modified }],
|
['websites', { includeTeams, onlyTeams, modified }],
|
||||||
@ -32,7 +43,7 @@ export function WebsitesDataTable({
|
|||||||
includeTeams,
|
includeTeams,
|
||||||
onlyTeams,
|
onlyTeams,
|
||||||
children,
|
children,
|
||||||
}) {
|
}: WebsitesDataTableProps) {
|
||||||
const queryResult = useWebsites({ includeTeams, onlyTeams });
|
const queryResult = useWebsites({ includeTeams, onlyTeams });
|
||||||
|
|
||||||
return (
|
return (
|
@ -3,12 +3,12 @@ import useMessages from 'components/hooks/useMessages';
|
|||||||
import PageHeader from 'components/layout/PageHeader';
|
import PageHeader from 'components/layout/PageHeader';
|
||||||
import WebsiteAddButton from './WebsiteAddButton';
|
import WebsiteAddButton from './WebsiteAddButton';
|
||||||
|
|
||||||
export function WebsitesHeader() {
|
export function WebsitesHeader({ showActions = true }) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader title={formatMessage(labels.websites)}>
|
<PageHeader title={formatMessage(labels.websites)}>
|
||||||
{!process.env.cloudMode && <WebsiteAddButton />}
|
{!process.env.cloudMode && showActions && <WebsiteAddButton />}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import WebsitesDataTable from './WebsitesDataTable';
|
import WebsitesDataTable from './WebsitesDataTable';
|
||||||
import WebsitesHeader from './WebsitesHeader';
|
import WebsitesHeader from './WebsitesHeader';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
if (process.env.cloudMode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WebsitesHeader />
|
<WebsitesHeader />
|
||||||
@ -13,3 +10,7 @@ export default function () {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Websites Settings | umami',
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import WebsiteList from '../settings/websites/WebsitesDataTable';
|
import WebsitesDataTable from '../settings/websites/WebsitesDataTable';
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages } from 'components/hooks';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Item, Tabs } from 'react-basics';
|
import { Item, Tabs } from 'react-basics';
|
||||||
@ -12,6 +12,7 @@ const TABS = {
|
|||||||
export function WebsitesBrowse() {
|
export function WebsitesBrowse() {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const [tab, setTab] = useState(TABS.myWebsites);
|
const [tab, setTab] = useState(TABS.myWebsites);
|
||||||
|
const allowEdit = !process.env.cloudMode;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -19,9 +20,9 @@ export function WebsitesBrowse() {
|
|||||||
<Item key={TABS.myWebsites}>{formatMessage(labels.myWebsites)}</Item>
|
<Item key={TABS.myWebsites}>{formatMessage(labels.myWebsites)}</Item>
|
||||||
<Item key={TABS.teamWebsites}>{formatMessage(labels.teamWebsites)}</Item>
|
<Item key={TABS.teamWebsites}>{formatMessage(labels.teamWebsites)}</Item>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{tab === TABS.myWebsites && <WebsiteList showHeader={false} />}
|
{tab === TABS.myWebsites && <WebsitesDataTable allowEdit={allowEdit} />}
|
||||||
{tab === TABS.teamWebsites && (
|
{tab === TABS.teamWebsites && (
|
||||||
<WebsiteList showHeader={false} showTeam={true} onlyTeams={true} />
|
<WebsitesDataTable showTeam={true} onlyTeams={true} allowEdit={allowEdit} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import WebsitesHeader from '../../(main)/settings/websites/WebsitesHeader';
|
|
||||||
import WebsitesBrowse from './WebsitesBrowse';
|
|
||||||
|
|
||||||
export default function WebsitesPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<WebsitesHeader />
|
|
||||||
<WebsitesBrowse />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
16
src/app/(main)/websites/page.tsx
Normal file
16
src/app/(main)/websites/page.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import WebsitesHeader from 'app/(main)/settings/websites/WebsitesHeader';
|
||||||
|
import WebsitesBrowse from './WebsitesBrowse';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
|
export default function WebsitesPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<WebsitesHeader showActions={false} />
|
||||||
|
<WebsitesBrowse />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Websites | umami',
|
||||||
|
};
|
@ -45,7 +45,7 @@ export function DataTable({
|
|||||||
const noResults = Boolean(!isLoading && query && !hasData);
|
const noResults = Boolean(!isLoading && query && !hasData);
|
||||||
|
|
||||||
const handleSearch = query => {
|
const handleSearch = query => {
|
||||||
setParams({ ...params, query });
|
setParams({ ...params, query, page: params.query ? page : 1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageChange = page => {
|
const handlePageChange = page => {
|
||||||
|
@ -14,8 +14,6 @@ export interface TeamsRequestBody {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MyTeamsRequestQuery extends SearchFilter {}
|
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
GET: yup.object().shape({
|
GET: yup.object().shape({
|
||||||
...pageInfo,
|
...pageInfo,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user