mirror of
https://github.com/kremalicious/umami.git
synced 2024-06-28 08:47:50 +02:00
Refactored queries.
This commit is contained in:
parent
18e36aa7b3
commit
b16f5cc067
|
@ -6,31 +6,30 @@ import Icons from 'components/icons';
|
|||
import ThemeButton from 'components/input/ThemeButton';
|
||||
import LanguageButton from 'components/input/LanguageButton';
|
||||
import ProfileButton from 'components/input/ProfileButton';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { useMessages, useNavigation } from 'components/hooks';
|
||||
import HamburgerButton from 'components/common/HamburgerButton';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import styles from './NavBar.module.css';
|
||||
|
||||
export function NavBar() {
|
||||
const pathname = usePathname();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const cloudMode = Boolean(process.env.cloudMode);
|
||||
const { pathname, renderTeamUrl } = useNavigation();
|
||||
|
||||
const links = [
|
||||
{ label: formatMessage(labels.dashboard), url: '/dashboard' },
|
||||
{ label: formatMessage(labels.websites), url: '/websites' },
|
||||
{ label: formatMessage(labels.reports), url: '/reports' },
|
||||
{ label: formatMessage(labels.settings), url: '/settings' },
|
||||
{ label: formatMessage(labels.dashboard), url: renderTeamUrl('/dashboard') },
|
||||
{ label: formatMessage(labels.websites), url: renderTeamUrl('/websites') },
|
||||
{ label: formatMessage(labels.reports), url: renderTeamUrl('/reports') },
|
||||
{ label: formatMessage(labels.settings), url: renderTeamUrl('/settings') },
|
||||
].filter(n => n);
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
label: formatMessage(labels.dashboard),
|
||||
url: '/dashboard',
|
||||
url: renderTeamUrl('/dashboard'),
|
||||
},
|
||||
!cloudMode && {
|
||||
label: formatMessage(labels.settings),
|
||||
url: '/settings',
|
||||
url: renderTeamUrl('/settings'),
|
||||
children: [
|
||||
{
|
||||
label: formatMessage(labels.websites),
|
||||
|
|
|
@ -5,14 +5,14 @@ async function getEnabled() {
|
|||
return !!process.env.ENABLE_TEST_CONSOLE;
|
||||
}
|
||||
|
||||
export default async function ({ params: { id } }) {
|
||||
export default async function ({ params: { websiteId } }) {
|
||||
const enabled = await getEnabled();
|
||||
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <TestConsole websiteId={id?.[0]} />;
|
||||
return <TestConsole websiteId={websiteId?.[0]} />;
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
|
@ -3,8 +3,14 @@ import { useReports } from 'components/hooks';
|
|||
import ReportsTable from './ReportsTable';
|
||||
import DataTable from 'components/common/DataTable';
|
||||
|
||||
export default function ReportsDataTable({ websiteId }: { websiteId?: string }) {
|
||||
const queryResult = useReports(websiteId);
|
||||
export default function ReportsDataTable({
|
||||
websiteId,
|
||||
teamId,
|
||||
}: {
|
||||
websiteId?: string;
|
||||
teamId?: string;
|
||||
}) {
|
||||
const queryResult = useReports({ websiteId, teamId });
|
||||
|
||||
return (
|
||||
<DataTable queryResult={queryResult}>
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
'use client';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import { Button, Icon, Icons, Text } from 'react-basics';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Icon, Icons, Text } from 'react-basics';
|
||||
import { useMessages, useNavigation } from 'components/hooks';
|
||||
import LinkButton from 'components/common/LinkButton';
|
||||
|
||||
export function ReportsHeader() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const router = useRouter();
|
||||
|
||||
const handleClick = () => router.push('/reports/create');
|
||||
const { renderTeamUrl } = useNavigation();
|
||||
|
||||
return (
|
||||
<PageHeader title={formatMessage(labels.reports)}>
|
||||
<Button variant="primary" onClick={handleClick}>
|
||||
<LinkButton href={renderTeamUrl('/reports/create')} variant="primary">
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.createReport)}</Text>
|
||||
</Button>
|
||||
</LinkButton>
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import Funnel from 'assets/funnel.svg';
|
|||
import Lightbulb from 'assets/lightbulb.svg';
|
||||
import Magnet from 'assets/magnet.svg';
|
||||
import styles from './ReportTemplates.module.css';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { useMessages, useNavigation } from 'components/hooks';
|
||||
|
||||
function ReportItem({ title, description, url, icon }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
@ -32,26 +32,27 @@ function ReportItem({ title, description, url, icon }) {
|
|||
);
|
||||
}
|
||||
|
||||
export function ReportTemplates({ showHeader = true }) {
|
||||
export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { renderTeamUrl } = useNavigation();
|
||||
|
||||
const reports = [
|
||||
{
|
||||
title: formatMessage(labels.insights),
|
||||
description: formatMessage(labels.insightsDescription),
|
||||
url: '/reports/insights',
|
||||
url: renderTeamUrl('/reports/insights'),
|
||||
icon: <Lightbulb />,
|
||||
},
|
||||
{
|
||||
title: formatMessage(labels.funnel),
|
||||
description: formatMessage(labels.funnelDescription),
|
||||
url: '/reports/funnel',
|
||||
url: renderTeamUrl('/reports/funnel'),
|
||||
icon: <Funnel />,
|
||||
},
|
||||
{
|
||||
title: formatMessage(labels.retention),
|
||||
description: formatMessage(labels.retentionDescription),
|
||||
url: '/reports/retention',
|
||||
url: renderTeamUrl('/reports/retention'),
|
||||
icon: <Magnet />,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Metadata } from 'next';
|
||||
import RetentionReport from './RetentionReport';
|
||||
|
||||
export default function RetentionReportPage() {
|
||||
return <RetentionReport reportId={null} />;
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
export const metadata: Metadata = {
|
||||
title: 'Create Report | umami',
|
||||
};
|
|
@ -28,7 +28,7 @@ export function LanguageSetting() {
|
|||
items={options}
|
||||
value={locale}
|
||||
renderValue={renderValue}
|
||||
onChange={saveLocale}
|
||||
onChange={val => saveLocale(val as string)}
|
||||
allowSearch={true}
|
||||
onSearch={setSearch}
|
||||
menuProps={{ className: styles.menu }}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
Button,
|
||||
SubmitButton,
|
||||
} from 'react-basics';
|
||||
import { setValue } from 'store/cache';
|
||||
import { touch } from 'store/cache';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
|
||||
export function TeamAddForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) {
|
||||
|
@ -17,10 +17,10 @@ export function TeamAddForm({ onSave, onClose }: { onSave: () => void; onClose:
|
|||
mutationFn: (data: any) => post('/teams', data),
|
||||
});
|
||||
|
||||
const handleSubmit = async data => {
|
||||
const handleSubmit = async (data: any) => {
|
||||
mutate(data, {
|
||||
onSuccess: async () => {
|
||||
setValue('teams', Date.now());
|
||||
touch('teams');
|
||||
onSave?.();
|
||||
onClose?.();
|
||||
},
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
SubmitButton,
|
||||
} from 'react-basics';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { setValue } from 'store/cache';
|
||||
import { touch } from 'store/cache';
|
||||
|
||||
export function TeamJoinForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) {
|
||||
const { formatMessage, labels, getMessage } = useMessages();
|
||||
|
@ -20,7 +20,7 @@ export function TeamJoinForm({ onSave, onClose }: { onSave: () => void; onClose:
|
|||
const handleSubmit = async (data: any) => {
|
||||
mutate(data, {
|
||||
onSuccess: async () => {
|
||||
setValue('teams:members', Date.now());
|
||||
touch('teams:members');
|
||||
onSave?.();
|
||||
onClose?.();
|
||||
},
|
||||
|
|
|
@ -18,11 +18,11 @@ const generateId = () => getRandomChars(16);
|
|||
export function TeamEditForm({
|
||||
teamId,
|
||||
data,
|
||||
readOnly,
|
||||
allowEdit,
|
||||
}: {
|
||||
teamId: string;
|
||||
data?: { name: string; accessCode: string };
|
||||
readOnly?: boolean;
|
||||
allowEdit?: boolean;
|
||||
}) {
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
const { post, useMutation } = useApi();
|
||||
|
@ -57,22 +57,24 @@ export function TeamEditForm({
|
|||
<TextField value={teamId} readOnly allowCopy />
|
||||
</FormRow>
|
||||
<FormRow label={formatMessage(labels.name)}>
|
||||
{!readOnly && (
|
||||
{allowEdit && (
|
||||
<FormInput name="name" rules={{ required: formatMessage(labels.required) }}>
|
||||
<TextField />
|
||||
</FormInput>
|
||||
)}
|
||||
{readOnly && data.name}
|
||||
{!allowEdit && data.name}
|
||||
</FormRow>
|
||||
<FormRow label={formatMessage(labels.accessCode)}>
|
||||
<Flexbox gap={10}>
|
||||
<TextField value={accessCode} readOnly allowCopy />
|
||||
{!readOnly && (
|
||||
<Button onClick={handleRegenerate}>{formatMessage(labels.regenerate)}</Button>
|
||||
)}
|
||||
</Flexbox>
|
||||
</FormRow>
|
||||
{!readOnly && (
|
||||
{allowEdit && (
|
||||
<FormRow label={formatMessage(labels.accessCode)}>
|
||||
<Flexbox gap={10}>
|
||||
<TextField value={accessCode} readOnly allowCopy />
|
||||
{allowEdit && (
|
||||
<Button onClick={handleRegenerate}>{formatMessage(labels.regenerate)}</Button>
|
||||
)}
|
||||
</Flexbox>
|
||||
</FormRow>
|
||||
)}
|
||||
{allowEdit && (
|
||||
<FormButtons>
|
||||
<SubmitButton variant="primary">{formatMessage(labels.save)}</SubmitButton>
|
||||
</FormButtons>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
|
||||
import { setValue } from 'store/cache';
|
||||
import { touch } from 'store/cache';
|
||||
|
||||
export function TeamMemberRemoveButton({
|
||||
teamId,
|
||||
|
@ -22,7 +22,7 @@ export function TeamMemberRemoveButton({
|
|||
const handleRemoveTeamMember = () => {
|
||||
mutate(null, {
|
||||
onSuccess: () => {
|
||||
setValue('team:members', Date.now());
|
||||
touch('team:members');
|
||||
onSave?.();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,24 +1,13 @@
|
|||
import { useApi, useFilterQuery } from 'components/hooks';
|
||||
import DataTable from 'components/common/DataTable';
|
||||
import useCache from 'store/cache';
|
||||
import TeamMembersTable from './TeamMembersTable';
|
||||
import useTeamMembers from 'components/hooks/queries/useTeamMembers';
|
||||
|
||||
export function TeamMembers({ teamId, readOnly }: { teamId: string; readOnly: boolean }) {
|
||||
const { get } = useApi();
|
||||
const modified = useCache(state => state?.['team:members']);
|
||||
const queryResult = useFilterQuery({
|
||||
queryKey: ['team:members', { teamId, modified }],
|
||||
queryFn: params => {
|
||||
return get(`/teams/${teamId}/users`, {
|
||||
...params,
|
||||
});
|
||||
},
|
||||
enabled: !!teamId,
|
||||
});
|
||||
export function TeamMembers({ teamId, allowEdit }: { teamId: string; allowEdit: boolean }) {
|
||||
const queryResult = useTeamMembers(teamId);
|
||||
|
||||
return (
|
||||
<DataTable queryResult={queryResult}>
|
||||
{({ data }) => <TeamMembersTable data={data} teamId={teamId} readOnly={readOnly} />}
|
||||
{({ data }) => <TeamMembersTable data={data} teamId={teamId} allowEdit={allowEdit} />}
|
||||
</DataTable>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ import TeamMemberRemoveButton from './TeamMemberRemoveButton';
|
|||
export function TeamMembersTable({
|
||||
data = [],
|
||||
teamId,
|
||||
readOnly,
|
||||
allowEdit,
|
||||
}: {
|
||||
data: any[];
|
||||
teamId: string;
|
||||
readOnly: boolean;
|
||||
allowEdit: boolean;
|
||||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { user } = useLogin();
|
||||
|
@ -23,16 +23,20 @@ export function TeamMembersTable({
|
|||
|
||||
return (
|
||||
<GridTable data={data} cardMode={['xs', 'sm', 'md'].includes(breakpoint)}>
|
||||
<GridColumn name="username" label={formatMessage(labels.username)} />
|
||||
<GridColumn name="username" label={formatMessage(labels.username)}>
|
||||
{row => row?.user?.username}
|
||||
</GridColumn>
|
||||
<GridColumn name="role" label={formatMessage(labels.role)}>
|
||||
{row => roles[row?.teamUser?.[0]?.role]}
|
||||
{row => roles[row?.role]}
|
||||
</GridColumn>
|
||||
<GridColumn name="action" label=" " alignment="end">
|
||||
{row => {
|
||||
return (
|
||||
!readOnly &&
|
||||
row?.teamUser?.[0]?.role !== ROLES.teamOwner &&
|
||||
user?.id !== row?.id && <TeamMemberRemoveButton teamId={teamId} userId={row.id} />
|
||||
allowEdit &&
|
||||
row?.role !== ROLES.teamOwner &&
|
||||
user?.id !== row?.id && (
|
||||
<TeamMemberRemoveButton teamId={teamId} userId={row?.user?.id} />
|
||||
)
|
||||
);
|
||||
}}
|
||||
</GridColumn>
|
||||
|
|
|
@ -47,9 +47,9 @@ export function TeamSettings({ teamId }: { teamId: string }) {
|
|||
<Item key="websites">{formatMessage(labels.websites)}</Item>
|
||||
<Item key="data">{formatMessage(labels.data)}</Item>
|
||||
</Tabs>
|
||||
{tab === 'details' && <TeamEditForm teamId={teamId} data={team} readOnly={!canEdit} />}
|
||||
{tab === 'members' && <TeamMembers teamId={teamId} readOnly={!canEdit} />}
|
||||
{tab === 'websites' && <TeamWebsites teamId={teamId} readOnly={!canEdit} />}
|
||||
{tab === 'details' && <TeamEditForm teamId={teamId} data={team} allowEdit={canEdit} />}
|
||||
{tab === 'members' && <TeamMembers teamId={teamId} allowEdit={canEdit} />}
|
||||
{tab === 'websites' && <TeamWebsites teamId={teamId} allowEdit={canEdit} />}
|
||||
{canEdit && tab === 'data' && <TeamData teamId={teamId} />}
|
||||
</Flexbox>
|
||||
</TeamsContext.Provider>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import WebsitesDataTable from 'app/(main)/settings/websites/WebsitesDataTable';
|
||||
|
||||
export function TeamWebsites({ teamId }: { teamId: string; readOnly: boolean }) {
|
||||
return <WebsitesDataTable teamId={teamId} />;
|
||||
export function TeamWebsites({ teamId, allowEdit }: { teamId: string; allowEdit: boolean }) {
|
||||
return <WebsitesDataTable teamId={teamId} allowEdit={allowEdit} />;
|
||||
}
|
||||
|
||||
export default TeamWebsites;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
|
||||
import WebsiteAddForm from './WebsiteAddForm';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { setValue } from 'store/cache';
|
||||
import { touch } from 'store/cache';
|
||||
|
||||
export function WebsiteAddButton({ teamId, onSave }: { teamId: string; onSave?: () => void }) {
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
|
@ -9,7 +9,7 @@ export function WebsiteAddButton({ teamId, onSave }: { teamId: string; onSave?:
|
|||
|
||||
const handleSave = async () => {
|
||||
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||
setValue('websites', Date.now());
|
||||
touch('websites');
|
||||
onSave?.();
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import WebsitesHeader from './WebsitesHeader';
|
|||
|
||||
export default function Websites() {
|
||||
const { user } = useLogin();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebsitesHeader showActions={user.role !== 'view-only'} />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ReactNode } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Button, Text, Icon, Icons, GridTable, GridColumn, useBreakpoint } from 'react-basics';
|
||||
import { useMessages, useLogin } from 'components/hooks';
|
||||
import { useMessages, useLogin, useNavigation } from 'components/hooks';
|
||||
|
||||
export interface WebsitesTableProps {
|
||||
data: any[];
|
||||
|
@ -23,6 +23,7 @@ export function WebsitesTable({
|
|||
const { formatMessage, labels } = useMessages();
|
||||
const { user } = useLogin();
|
||||
const breakpoint = useBreakpoint();
|
||||
const { renderTeamUrl } = useNavigation();
|
||||
|
||||
return (
|
||||
<GridTable data={data} cardMode={['xs', 'sm', 'md'].includes(breakpoint)}>
|
||||
|
@ -46,7 +47,7 @@ export function WebsitesTable({
|
|||
</Link>
|
||||
)}
|
||||
{allowView && (
|
||||
<Link href={teamId ? `/teams/${teamId}/websites/${id}` : `/websites/${id}`}>
|
||||
<Link href={renderTeamUrl(`/websites/${id}`)}>
|
||||
<Button>
|
||||
<Icon>
|
||||
<Icons.External />
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import WebsiteSettings from '../WebsiteSettings';
|
||||
|
||||
export default async function WebsiteSettingsPage({ params: { id } }) {
|
||||
if (process.env.cloudMode) {
|
||||
export default async function WebsiteSettingsPage({ params: { websiteId } }) {
|
||||
if (process.env.cloudMode || !websiteId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <WebsiteSettings websiteId={id} />;
|
||||
return <WebsiteSettings websiteId={websiteId} />;
|
||||
}
|
||||
|
|
27
src/app/(main)/teams/[teamId]/reports/TeamReports.tsx
Normal file
27
src/app/(main)/teams/[teamId]/reports/TeamReports.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
'use client';
|
||||
import Link from 'next/link';
|
||||
import { Button, Flexbox, Icon, Icons, Text } from 'react-basics';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import ReportsDataTable from 'app/(main)/reports/ReportsDataTable';
|
||||
|
||||
export function TeamReports({ websiteId }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flexbox alignItems="center" justifyContent="end">
|
||||
<Link href={`/reports/create`}>
|
||||
<Button variant="primary">
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.createReport)}</Text>
|
||||
</Button>
|
||||
</Link>
|
||||
</Flexbox>
|
||||
<ReportsDataTable websiteId={websiteId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default TeamReports;
|
10
src/app/(main)/teams/[teamId]/reports/create/page.tsx
Normal file
10
src/app/(main)/teams/[teamId]/reports/create/page.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { Metadata } from 'next';
|
||||
import ReportTemplates from 'app/(main)/reports/create/ReportTemplates';
|
||||
|
||||
export default function () {
|
||||
return <ReportTemplates />;
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Create Report | umami',
|
||||
};
|
15
src/app/(main)/teams/[teamId]/reports/page.tsx
Normal file
15
src/app/(main)/teams/[teamId]/reports/page.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Metadata } from 'next';
|
||||
import ReportsHeader from 'app/(main)/reports/ReportsHeader';
|
||||
import ReportsDataTable from 'app/(main)/reports/ReportsDataTable';
|
||||
|
||||
export default function ({ params: { teamId } }) {
|
||||
return (
|
||||
<>
|
||||
<ReportsHeader />
|
||||
<ReportsDataTable teamId={teamId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
export const metadata: Metadata = {
|
||||
title: 'Reports | umami',
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import WebsiteDetails from '../../../../websites/[websiteId]/WebsiteDetails';
|
||||
import WebsiteDetails from 'app/(main)/websites/[websiteId]/WebsiteDetails';
|
||||
|
||||
export default function TeamWebsitePage({ params: { websiteId } }) {
|
||||
return <WebsiteDetails websiteId={websiteId} />;
|
||||
|
|
|
@ -43,7 +43,7 @@ export default function WebsiteExpandedView({
|
|||
const { formatMessage, labels } = useMessages();
|
||||
const {
|
||||
router,
|
||||
makeUrl,
|
||||
renderUrl,
|
||||
pathname,
|
||||
query: { view },
|
||||
} = useNavigation();
|
||||
|
@ -52,69 +52,69 @@ export default function WebsiteExpandedView({
|
|||
{
|
||||
key: 'url',
|
||||
label: formatMessage(labels.pages),
|
||||
url: makeUrl({ view: 'url' }),
|
||||
url: renderUrl({ view: 'url' }),
|
||||
},
|
||||
{
|
||||
key: 'referrer',
|
||||
label: formatMessage(labels.referrers),
|
||||
url: makeUrl({ view: 'referrer' }),
|
||||
url: renderUrl({ view: 'referrer' }),
|
||||
},
|
||||
{
|
||||
key: 'browser',
|
||||
label: formatMessage(labels.browsers),
|
||||
url: makeUrl({ view: 'browser' }),
|
||||
url: renderUrl({ view: 'browser' }),
|
||||
},
|
||||
{
|
||||
key: 'os',
|
||||
label: formatMessage(labels.os),
|
||||
url: makeUrl({ view: 'os' }),
|
||||
url: renderUrl({ view: 'os' }),
|
||||
},
|
||||
{
|
||||
key: 'device',
|
||||
label: formatMessage(labels.devices),
|
||||
url: makeUrl({ view: 'device' }),
|
||||
url: renderUrl({ view: 'device' }),
|
||||
},
|
||||
{
|
||||
key: 'country',
|
||||
label: formatMessage(labels.countries),
|
||||
url: makeUrl({ view: 'country' }),
|
||||
url: renderUrl({ view: 'country' }),
|
||||
},
|
||||
{
|
||||
key: 'region',
|
||||
label: formatMessage(labels.regions),
|
||||
url: makeUrl({ view: 'region' }),
|
||||
url: renderUrl({ view: 'region' }),
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: formatMessage(labels.cities),
|
||||
url: makeUrl({ view: 'city' }),
|
||||
url: renderUrl({ view: 'city' }),
|
||||
},
|
||||
{
|
||||
key: 'language',
|
||||
label: formatMessage(labels.languages),
|
||||
url: makeUrl({ view: 'language' }),
|
||||
url: renderUrl({ view: 'language' }),
|
||||
},
|
||||
{
|
||||
key: 'screen',
|
||||
label: formatMessage(labels.screens),
|
||||
url: makeUrl({ view: 'screen' }),
|
||||
url: renderUrl({ view: 'screen' }),
|
||||
},
|
||||
{
|
||||
key: 'event',
|
||||
label: formatMessage(labels.events),
|
||||
url: makeUrl({ view: 'event' }),
|
||||
url: renderUrl({ view: 'event' }),
|
||||
},
|
||||
{
|
||||
key: 'query',
|
||||
label: formatMessage(labels.queryParameters),
|
||||
url: makeUrl({ view: 'query' }),
|
||||
url: renderUrl({ view: 'query' }),
|
||||
},
|
||||
];
|
||||
|
||||
const DetailsComponent = views[view] || (() => null);
|
||||
|
||||
const handleChange = (view: any) => {
|
||||
router.push(makeUrl({ view }));
|
||||
router.push(renderUrl({ view }));
|
||||
};
|
||||
|
||||
const renderValue = (value: string) => items.find(({ key }) => key === value)?.label;
|
||||
|
|
|
@ -11,7 +11,7 @@ export function WebsiteFilterButton({
|
|||
className?: string;
|
||||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { makeUrl, router } = useNavigation();
|
||||
const { renderUrl, router } = useNavigation();
|
||||
|
||||
const fieldOptions = [
|
||||
{ name: 'url', type: 'string', label: formatMessage(labels.url) },
|
||||
|
@ -25,7 +25,7 @@ export function WebsiteFilterButton({
|
|||
];
|
||||
|
||||
const handleAddFilter = ({ name, value }) => {
|
||||
router.push(makeUrl({ [name]: value }));
|
||||
router.push(renderUrl({ [name]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,7 +6,7 @@ import { DATA_TYPES } from 'lib/constants';
|
|||
|
||||
export function EventDataTable({ data = [] }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { makeUrl } = useNavigation();
|
||||
const { renderUrl } = useNavigation();
|
||||
|
||||
if (data.length === 0) {
|
||||
return <Empty />;
|
||||
|
@ -16,7 +16,7 @@ export function EventDataTable({ data = [] }) {
|
|||
<GridTable data={data}>
|
||||
<GridColumn name="eventName" label={formatMessage(labels.event)}>
|
||||
{row => (
|
||||
<Link href={makeUrl({ event: row.eventName })} shallow={true}>
|
||||
<Link href={renderUrl({ event: row.eventName })} shallow={true}>
|
||||
{row.eventName}
|
||||
</Link>
|
||||
)}
|
||||
|
|
|
@ -8,12 +8,12 @@ import { DATA_TYPES } from 'lib/constants';
|
|||
|
||||
export function EventDataValueTable({ data = [], event }: { data: any[]; event: string }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { makeUrl } = useNavigation();
|
||||
const { renderUrl } = useNavigation();
|
||||
|
||||
const Title = () => {
|
||||
return (
|
||||
<>
|
||||
<Link href={makeUrl({ event: undefined })}>
|
||||
<Link href={renderUrl({ event: undefined })}>
|
||||
<Button>
|
||||
<Icon rotate={180}>
|
||||
<Icons.ArrowRight />
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import WebsiteHeader from '../WebsiteHeader';
|
||||
import WebsiteEventData from './WebsiteEventData';
|
||||
|
||||
export default function WebsiteEventDataPage({ params: { id } }) {
|
||||
if (!id) {
|
||||
export default function WebsiteEventDataPage({ params: { websiteId } }) {
|
||||
if (!websiteId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebsiteHeader websiteId={id} />
|
||||
<WebsiteEventData websiteId={id} />
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<WebsiteEventData websiteId={websiteId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import WebsiteDetails from './WebsiteDetails';
|
||||
|
||||
export default function WebsitePage({ params: { id } }) {
|
||||
return <WebsiteDetails websiteId={id} />;
|
||||
export default function WebsitePage({ params: { websiteId } }) {
|
||||
return <WebsiteDetails websiteId={websiteId} />;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Realtime from './Realtime';
|
||||
|
||||
export default function WebsiteRealtimePage({ params: { id } }) {
|
||||
if (!id) {
|
||||
export default function WebsiteRealtimePage({ params: { websiteId } }) {
|
||||
if (!websiteId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Realtime websiteId={id} />;
|
||||
return <Realtime websiteId={websiteId} />;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import WebsiteReports from './WebsiteReports';
|
||||
|
||||
export default function WebsiteReportsPage({ params: { id } }) {
|
||||
if (!id) {
|
||||
export default function WebsiteReportsPage({ params: { websiteId } }) {
|
||||
if (!websiteId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <WebsiteReports websiteId={id} />;
|
||||
return <WebsiteReports websiteId={websiteId} />;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export function FilterLink({
|
|||
className,
|
||||
}: FilterLinkProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { makeUrl, query } = useNavigation();
|
||||
const { renderUrl, query } = useNavigation();
|
||||
const active = query[id] !== undefined;
|
||||
const selected = query[id] === value;
|
||||
|
||||
|
@ -39,7 +39,7 @@ export function FilterLink({
|
|||
{children}
|
||||
{!value && `(${label || formatMessage(labels.unknown)})`}
|
||||
{value && (
|
||||
<Link href={makeUrl({ [id]: value })} className={styles.label} replace>
|
||||
<Link href={renderUrl({ [id]: value })} className={styles.label} replace>
|
||||
{safeDecodeURI(label || value)}
|
||||
</Link>
|
||||
)}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import { useState } from 'react';
|
||||
import useApi from './useApi';
|
||||
import useFilterQuery from './useFilterQuery';
|
||||
import useCache from 'store/cache';
|
||||
|
||||
export function useReports(websiteId?: string) {
|
||||
const [modified, setModified] = useState(Date.now());
|
||||
export function useReports({ websiteId, teamId }: { websiteId?: string; teamId?: string }) {
|
||||
const modified = useCache((state: any) => state?.reports);
|
||||
const { get, del, useMutation } = useApi();
|
||||
const { mutate } = useMutation({ mutationFn: (reportId: string) => del(`/reports/${reportId}`) });
|
||||
const queryResult = useFilterQuery({
|
||||
queryKey: ['reports', { websiteId, modified }],
|
||||
queryFn: (params: any) => {
|
||||
return get(websiteId ? `/websites/${websiteId}/reports` : `/reports`, params);
|
||||
const url = websiteId ? `/websites/${websiteId}/reports` : `/reports`;
|
||||
|
||||
return get(teamId ? `/teams/${teamId}${url}` : url, params);
|
||||
},
|
||||
});
|
||||
const { mutate } = useMutation({ mutationFn: (reportId: string) => del(`/reports/${reportId}`) });
|
||||
|
||||
const deleteReport = (id: any) => {
|
||||
mutate(id, {
|
||||
onSuccess: () => {
|
||||
setModified(Date.now());
|
||||
},
|
||||
const deleteReport = (reportId: any) => {
|
||||
mutate(reportId, {
|
||||
onSuccess: () => {},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
16
src/components/hooks/queries/useTeamMembers.ts
Normal file
16
src/components/hooks/queries/useTeamMembers.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import useApi from './useApi';
|
||||
import useFilterQuery from './useFilterQuery';
|
||||
|
||||
export function useTeamMembers(teamId: string) {
|
||||
const { get } = useApi();
|
||||
|
||||
return useFilterQuery({
|
||||
queryKey: ['teams:users', { teamId }],
|
||||
queryFn: (params: any) => {
|
||||
return get(`/teams/${teamId}/users`, params);
|
||||
},
|
||||
enabled: !!teamId,
|
||||
});
|
||||
}
|
||||
|
||||
export default useTeamMembers;
|
|
@ -10,7 +10,7 @@ const messages = {
|
|||
'en-US': enUS,
|
||||
};
|
||||
|
||||
const selector = state => state.locale;
|
||||
const selector = (state: { locale: any }) => state.locale;
|
||||
|
||||
export function useLocale() {
|
||||
const locale = useStore(selector);
|
||||
|
@ -18,7 +18,7 @@ export function useLocale() {
|
|||
const dir = getTextDirection(locale);
|
||||
const dateLocale = getDateLocale(locale);
|
||||
|
||||
async function loadMessages(locale) {
|
||||
async function loadMessages(locale: string) {
|
||||
const { ok, data } = await httpGet(`${process.env.basePath}/intl/messages/${locale}.json`);
|
||||
|
||||
if (ok) {
|
||||
|
@ -26,7 +26,7 @@ export function useLocale() {
|
|||
}
|
||||
}
|
||||
|
||||
async function saveLocale(value) {
|
||||
async function saveLocale(value: string) {
|
||||
if (!messages[value]) {
|
||||
await loadMessages(value);
|
||||
}
|
||||
|
|
|
@ -6,11 +6,14 @@ export function useNavigation(): {
|
|||
pathname: string;
|
||||
query: { [key: string]: string };
|
||||
router: any;
|
||||
makeUrl: (params: any, reset?: boolean) => string;
|
||||
renderUrl: (params: any, reset?: boolean) => string;
|
||||
renderTeamUrl: (url: string) => string;
|
||||
teamId?: string;
|
||||
} {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const params = useSearchParams();
|
||||
const [, teamId] = pathname.match(/^\/teams\/([a-f0-9-]+)/) || [];
|
||||
|
||||
const query = useMemo(() => {
|
||||
const obj = {};
|
||||
|
@ -22,11 +25,15 @@ export function useNavigation(): {
|
|||
return obj;
|
||||
}, [params]);
|
||||
|
||||
function makeUrl(params: any, reset?: boolean) {
|
||||
function renderUrl(params: any, reset?: boolean) {
|
||||
return reset ? pathname : buildUrl(pathname, { ...query, ...params });
|
||||
}
|
||||
|
||||
return { pathname, query, router, makeUrl };
|
||||
function renderTeamUrl(url: string) {
|
||||
return teamId ? `/teams/${teamId}${url}` : url;
|
||||
}
|
||||
|
||||
return { pathname, query, router, renderUrl, renderTeamUrl, teamId };
|
||||
}
|
||||
|
||||
export default useNavigation;
|
||||
|
|
|
@ -10,7 +10,7 @@ export function FilterTags({ params }) {
|
|||
const { formatValue } = useFormat();
|
||||
const {
|
||||
router,
|
||||
makeUrl,
|
||||
renderUrl,
|
||||
query: { view },
|
||||
} = useNavigation();
|
||||
|
||||
|
@ -19,11 +19,11 @@ export function FilterTags({ params }) {
|
|||
}
|
||||
|
||||
function handleCloseFilter(param?: string) {
|
||||
router.push(makeUrl({ [param]: undefined }));
|
||||
router.push(renderUrl({ [param]: undefined }));
|
||||
}
|
||||
|
||||
function handleResetFilter() {
|
||||
router.push(makeUrl({ view }, true));
|
||||
router.push(renderUrl({ view }, true));
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -45,7 +45,7 @@ export function MetricsTable({
|
|||
const { formatValue } = useFormat();
|
||||
const [{ startDate, endDate, modified }] = useDateRange(websiteId);
|
||||
const {
|
||||
makeUrl,
|
||||
renderUrl,
|
||||
query: { url, referrer, title, os, browser, device, country, region, city },
|
||||
} = useNavigation();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
@ -142,7 +142,7 @@ export function MetricsTable({
|
|||
{!data && isLoading && !isFetched && <Loading icon="dots" />}
|
||||
<div className={styles.footer}>
|
||||
{data && !error && limit && (
|
||||
<LinkButton href={makeUrl({ view: type })} variant="quiet">
|
||||
<LinkButton href={renderUrl({ view: type })} variant="quiet">
|
||||
<Text>{formatMessage(labels.more)}</Text>
|
||||
<Icon size="sm" rotate={dir === 'rtl' ? 180 : 0}>
|
||||
<Icons.ArrowRight />
|
||||
|
|
|
@ -12,13 +12,13 @@ export interface PagesTableProps extends MetricsTableProps {
|
|||
export function PagesTable({ allowFilter, domainName, ...props }: PagesTableProps) {
|
||||
const {
|
||||
router,
|
||||
makeUrl,
|
||||
renderUrl,
|
||||
query: { view = 'url' },
|
||||
} = useNavigation();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
const handleSelect = (key: any) => {
|
||||
router.push(makeUrl({ view: key }), { scroll: true });
|
||||
router.push(renderUrl({ view: key }), { scroll: true });
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { User, Website } from '@prisma/client';
|
||||
import redis from '@umami/redis-client';
|
||||
import { getSession, getUserById, getWebsiteById } from '../queries';
|
||||
import { getSession, getUser, getWebsite } from '../queries';
|
||||
|
||||
async function fetchWebsite(id): Promise<Website> {
|
||||
return redis.client.getCache(`website:${id}`, () => getWebsiteById(id), 86400);
|
||||
async function fetchWebsite(websiteId: string): Promise<Website> {
|
||||
return redis.client.getCache(`website:${websiteId}`, () => getWebsite(websiteId), 86400);
|
||||
}
|
||||
|
||||
async function storeWebsite(data) {
|
||||
async function storeWebsite(data: { id: any }) {
|
||||
const { id } = data;
|
||||
const key = `website:${id}`;
|
||||
|
||||
|
@ -21,11 +21,7 @@ async function deleteWebsite(id) {
|
|||
}
|
||||
|
||||
async function fetchUser(id): Promise<User> {
|
||||
return redis.client.getCache(
|
||||
`user:${id}`,
|
||||
() => getUserById(id, { includePassword: true }),
|
||||
86400,
|
||||
);
|
||||
return redis.client.getCache(`user:${id}`, () => getUser(id, { includePassword: true }), 86400);
|
||||
}
|
||||
|
||||
async function storeUser(data) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import cache from 'lib/cache';
|
||||
import { getSession, getUserById, getWebsiteById } from 'queries';
|
||||
import { getSession, getUser, getWebsite } from 'queries';
|
||||
import { User, Website, Session } from '@prisma/client';
|
||||
|
||||
export async function loadWebsite(websiteId: string): Promise<Website> {
|
||||
|
@ -8,7 +8,7 @@ export async function loadWebsite(websiteId: string): Promise<Website> {
|
|||
if (cache.enabled) {
|
||||
website = await cache.fetchWebsite(websiteId);
|
||||
} else {
|
||||
website = await getWebsiteById(websiteId);
|
||||
website = await getWebsite(websiteId);
|
||||
}
|
||||
|
||||
if (!website || website.deletedAt) {
|
||||
|
@ -40,7 +40,7 @@ export async function loadUser(userId: string): Promise<User> {
|
|||
if (cache.enabled) {
|
||||
user = await cache.fetchUser(userId);
|
||||
} else {
|
||||
user = await getUserById(userId);
|
||||
user = await getUser(userId);
|
||||
}
|
||||
|
||||
if (!user || user.deletedAt) {
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
unauthorized,
|
||||
} from 'next-basics';
|
||||
import { NextApiRequestCollect } from 'pages/api/send';
|
||||
import { getUserById } from '../queries';
|
||||
import { getUser } from '../queries';
|
||||
|
||||
const log = debug('umami:middleware');
|
||||
|
||||
|
@ -57,12 +57,12 @@ export const useAuth = createMiddleware(async (req, res, next) => {
|
|||
const { userId, authKey, grant } = payload || {};
|
||||
|
||||
if (userId) {
|
||||
user = await getUserById(userId);
|
||||
user = await getUser(userId);
|
||||
} else if (redis.enabled && authKey) {
|
||||
const key = await redis.client.get(authKey);
|
||||
|
||||
if (key?.userId) {
|
||||
user = await getUserById(key.userId);
|
||||
user = await getUser(key.userId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -175,24 +175,12 @@ async function rawQuery(sql: string, data: object): Promise<any> {
|
|||
return prisma.rawQuery(query, params);
|
||||
}
|
||||
|
||||
function getPageFilters(filters: SearchFilter): [
|
||||
{
|
||||
orderBy: {
|
||||
[x: string]: string;
|
||||
}[];
|
||||
take: number;
|
||||
skip: number;
|
||||
},
|
||||
{
|
||||
pageSize: number;
|
||||
page: number;
|
||||
orderBy: string;
|
||||
},
|
||||
] {
|
||||
async function pagedQuery<T>(model: string, criteria: T, filters: SearchFilter) {
|
||||
const { page = 1, pageSize = DEFAULT_PAGE_SIZE, orderBy, sortDescending = false } = filters || {};
|
||||
|
||||
return [
|
||||
{
|
||||
const data = await prisma.client[model].findMany({
|
||||
...criteria,
|
||||
...{
|
||||
...(pageSize > 0 && { take: +pageSize, skip: +pageSize * (page - 1) }),
|
||||
...(orderBy && {
|
||||
orderBy: [
|
||||
|
@ -202,8 +190,11 @@ function getPageFilters(filters: SearchFilter): [
|
|||
],
|
||||
}),
|
||||
},
|
||||
{ page: +page, pageSize, orderBy },
|
||||
];
|
||||
});
|
||||
|
||||
const count = await prisma.client[model].count({ where: (criteria as any).where });
|
||||
|
||||
return { data, count, page: +page, pageSize, orderBy };
|
||||
}
|
||||
|
||||
function getQueryMode(): Prisma.QueryMode {
|
||||
|
@ -225,7 +216,7 @@ export default {
|
|||
getTimestampDiffQuery,
|
||||
getFilterQuery,
|
||||
parseFilters,
|
||||
getPageFilters,
|
||||
getQueryMode,
|
||||
rawQuery,
|
||||
pagedQuery,
|
||||
};
|
||||
|
|
|
@ -38,6 +38,10 @@ export interface TeamSearchFilter extends SearchFilter {
|
|||
userId?: string;
|
||||
}
|
||||
|
||||
export interface TeamUserSearchFilter extends SearchFilter {
|
||||
teamId?: string;
|
||||
}
|
||||
|
||||
export interface ReportSearchFilter extends SearchFilter {
|
||||
userId?: string;
|
||||
websiteId?: string;
|
||||
|
|
|
@ -42,10 +42,7 @@ export default async (
|
|||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const { page, query, pageSize } = req.query;
|
||||
|
||||
const users = await getUsers(
|
||||
{ page, query, pageSize: +pageSize || undefined },
|
||||
{
|
||||
include: {
|
||||
_count: {
|
||||
|
@ -57,6 +54,7 @@ export default async (
|
|||
},
|
||||
},
|
||||
},
|
||||
req.query,
|
||||
);
|
||||
|
||||
return ok(res, users);
|
||||
|
|
|
@ -39,25 +39,19 @@ export default async (
|
|||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const websites = await getWebsites(req.query, {
|
||||
include: {
|
||||
teamWebsite: {
|
||||
include: {
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
const websites = await getWebsites(
|
||||
{
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
username: true,
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
select: {
|
||||
username: true,
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
req.query,
|
||||
);
|
||||
|
||||
return ok(res, websites);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,16 @@ import { NextApiRequestAuth } from 'lib/types';
|
|||
import { useAuth } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { ok } from 'next-basics';
|
||||
import { getUserTeams } from 'queries/admin/team';
|
||||
|
||||
export default async (req: NextApiRequestAuth, res: NextApiResponse) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
return ok(res, req.auth.user);
|
||||
const { user } = req.auth;
|
||||
|
||||
const teams = await getUserTeams(user.id);
|
||||
|
||||
user['teams'] = teams.data.map(n => n);
|
||||
|
||||
return ok(res, user);
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
methodNotAllowed,
|
||||
ok,
|
||||
} from 'next-basics';
|
||||
import { getUserById, updateUser } from 'queries';
|
||||
import { getUser, updateUser } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserPasswordRequestQuery {
|
||||
|
@ -43,7 +43,7 @@ export default async (
|
|||
const { id } = req.auth.user;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const user = await getUserById(id, { includePassword: true });
|
||||
const user = await getUser(id, { includePassword: true });
|
||||
|
||||
if (!checkPassword(currentPassword, user.password)) {
|
||||
return badRequest(res, 'Current password is incorrect');
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useAuth, useCors, useValidate } from 'lib/middleware';
|
|||
import { NextApiRequestQueryBody, ReportType, YupRequest } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { deleteReport, getReportById, updateReport } from 'queries';
|
||||
import { deleteReport, getReport, updateReport } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface ReportRequestQuery {
|
||||
|
@ -54,7 +54,7 @@ export default async (
|
|||
} = req.auth;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const report = await getReportById(reportId);
|
||||
const report = await getReport(reportId);
|
||||
|
||||
if (!(await canViewReport(req.auth, report))) {
|
||||
return unauthorized(res);
|
||||
|
@ -68,7 +68,7 @@ export default async (
|
|||
if (req.method === 'POST') {
|
||||
const { websiteId, type, name, description, parameters } = req.body;
|
||||
|
||||
const report = await getReportById(reportId);
|
||||
const report = await getReport(reportId);
|
||||
|
||||
if (!(await canUpdateReport(req.auth, report))) {
|
||||
return unauthorized(res);
|
||||
|
@ -87,7 +87,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
const report = await getReportById(reportId);
|
||||
const report = await getReport(reportId);
|
||||
|
||||
if (!(await canDeleteReport(req.auth, report))) {
|
||||
return unauthorized(res);
|
||||
|
|
|
@ -4,7 +4,7 @@ import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
|
|||
import { pageInfo } from 'lib/schema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok } from 'next-basics';
|
||||
import { createReport, getReportsByUserId } from 'queries';
|
||||
import { createReport, getUserReports } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface ReportsRequestQuery extends SearchFilter {}
|
||||
|
@ -52,11 +52,10 @@ export default async (
|
|||
if (req.method === 'GET') {
|
||||
const { page, query, pageSize } = req.query;
|
||||
|
||||
const data = await getReportsByUserId(userId, {
|
||||
const data = await getUserReports(userId, {
|
||||
page,
|
||||
pageSize: +pageSize || undefined,
|
||||
query,
|
||||
includeTeams: true,
|
||||
});
|
||||
|
||||
return ok(res, data);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useValidate } from 'lib/middleware';
|
|||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createToken, methodNotAllowed, notFound, ok } from 'next-basics';
|
||||
import { getWebsiteByShareId } from 'queries';
|
||||
import { getSharedWebsite } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface ShareRequestQuery {
|
||||
|
@ -30,7 +30,7 @@ export default async (
|
|||
const { id: shareId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const website = await getWebsiteByShareId(shareId);
|
||||
const website = await getSharedWebsite(shareId);
|
||||
|
||||
if (website) {
|
||||
const data = { websiteId: website.id };
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useAuth, useValidate } from 'lib/middleware';
|
|||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, notFound, ok, unauthorized } from 'next-basics';
|
||||
import { deleteTeam, getTeamById, updateTeam } from 'queries';
|
||||
import { deleteTeam, getTeam, updateTeam } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface TeamRequestQuery {
|
||||
|
@ -44,7 +44,7 @@ export default async (
|
|||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const team = await getTeamById(teamId, { includeTeamUser: true });
|
||||
const team = await getTeam(teamId, { includeMembers: true });
|
||||
|
||||
if (!team) {
|
||||
return notFound(res);
|
||||
|
|
|
@ -4,7 +4,7 @@ import { pageInfo } from 'lib/schema';
|
|||
import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createTeamUser, getTeamUser, getUsersByTeamId } from 'queries';
|
||||
import { createTeamUser, getTeamUser, getTeamUsers } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface TeamUserRequestQuery extends SearchFilter {
|
||||
|
@ -46,7 +46,7 @@ export default async (
|
|||
|
||||
const { query, page, pageSize } = req.query;
|
||||
|
||||
const users = await getUsersByTeamId(teamId, {
|
||||
const users = await getTeamUsers(teamId, {
|
||||
query,
|
||||
page,
|
||||
pageSize: +pageSize || undefined,
|
||||
|
|
|
@ -5,7 +5,7 @@ import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
|
|||
import { pageInfo } from 'lib/schema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createWebsite, getWebsitesByTeamId } from 'queries';
|
||||
import { createWebsite, getTeamWebsites } from 'queries';
|
||||
import { uuid } from 'lib/crypto';
|
||||
|
||||
export interface TeamWebsiteRequestQuery extends SearchFilter {
|
||||
|
@ -46,7 +46,7 @@ export default async (
|
|||
|
||||
const { page, query, pageSize } = req.query;
|
||||
|
||||
const websites = await getWebsitesByTeamId(teamId, {
|
||||
const websites = await getTeamWebsites(teamId, {
|
||||
page,
|
||||
query,
|
||||
pageSize: +pageSize || undefined,
|
||||
|
|
|
@ -6,7 +6,7 @@ import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
|
|||
import { pageInfo } from 'lib/schema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { getRandomChars, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createTeam, getTeamsByUserId } from 'queries';
|
||||
import { createTeam, getUserTeams } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface TeamsRequestQuery extends SearchFilter {}
|
||||
|
@ -37,7 +37,7 @@ export default async (
|
|||
if (req.method === 'GET') {
|
||||
const { page, query, pageSize } = req.query;
|
||||
|
||||
const results = await getTeamsByUserId(userId, {
|
||||
const results = await getUserTeams(userId, {
|
||||
page,
|
||||
query,
|
||||
pageSize: +pageSize || undefined,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useAuth, useValidate } from 'lib/middleware';
|
|||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, notFound, ok } from 'next-basics';
|
||||
import { createTeamUser, getTeamByAccessCode, getTeamUser } from 'queries';
|
||||
import { createTeamUser, findTeam, getTeamUser } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
export interface TeamsJoinRequestBody {
|
||||
accessCode: string;
|
||||
|
@ -26,7 +26,11 @@ export default async (
|
|||
if (req.method === 'POST') {
|
||||
const { accessCode } = req.body;
|
||||
|
||||
const team = await getTeamByAccessCode(accessCode);
|
||||
const team = await findTeam({
|
||||
where: {
|
||||
accessCode,
|
||||
},
|
||||
});
|
||||
|
||||
if (!team) {
|
||||
return notFound(res, 'message.team-not-found');
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useAuth, useValidate } from 'lib/middleware';
|
|||
import { NextApiRequestQueryBody, Role, User } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { deleteUser, getUserById, getUserByUsername, updateUser } from 'queries';
|
||||
import { deleteUser, getUser, getUserByUsername, updateUser } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserRequestQuery {
|
||||
|
@ -45,7 +45,7 @@ export default async (
|
|||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const user = await getUserById(id);
|
||||
const user = await getUser(id);
|
||||
|
||||
return ok(res, user);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ export default async (
|
|||
|
||||
const { username, password, role } = req.body;
|
||||
|
||||
const user = await getUserById(id);
|
||||
const user = await getUser(id);
|
||||
|
||||
const data: any = {};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
|
|||
import { pageInfo } from 'lib/schema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getTeamsByUserId } from 'queries';
|
||||
import { getUserTeams } from 'queries';
|
||||
|
||||
export interface UserTeamsRequestQuery extends SearchFilter {
|
||||
id: string;
|
||||
|
@ -41,7 +41,7 @@ export default async (
|
|||
|
||||
const { page, query, pageSize } = req.query;
|
||||
|
||||
const teams = await getTeamsByUserId(userId, {
|
||||
const teams = await getUserTeams(userId, {
|
||||
query,
|
||||
page,
|
||||
pageSize: +pageSize || undefined,
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useAuth, useCors, useValidate } from 'lib/middleware';
|
|||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getEventDataUsage, getEventUsage, getUserWebsites } from 'queries';
|
||||
import { getAllWebsites, getEventDataUsage, getEventUsage } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserUsageRequestQuery {
|
||||
|
@ -26,7 +26,7 @@ const schema = {
|
|||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref<number>('startAt')).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -50,7 +50,7 @@ export default async (
|
|||
const startDate = new Date(+startAt);
|
||||
const endDate = new Date(+endAt);
|
||||
|
||||
const websites = await getUserWebsites(userId);
|
||||
const websites = await getAllWebsites(userId);
|
||||
|
||||
const websiteIds = websites.map(a => a.id);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
|
|||
import { pageInfo } from 'lib/schema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getWebsitesByUserId } from 'queries';
|
||||
import { getUserWebsites } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserWebsitesRequestQuery extends SearchFilter {
|
||||
|
@ -37,7 +37,7 @@ export default async (
|
|||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const websites = await getWebsitesByUserId(userId, {
|
||||
const websites = await getUserWebsites(userId, {
|
||||
page,
|
||||
pageSize: +pageSize || undefined,
|
||||
query,
|
||||
|
|
|
@ -3,7 +3,7 @@ import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics';
|
|||
import { Website, NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite, canUpdateWebsite, canDeleteWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { deleteWebsite, getWebsiteById, updateWebsite } from 'queries';
|
||||
import { deleteWebsite, getWebsite, updateWebsite } from 'queries';
|
||||
import { SHARE_ID_REGEX } from 'lib/constants';
|
||||
|
||||
export interface WebsiteRequestQuery {
|
||||
|
@ -45,7 +45,7 @@ export default async (
|
|||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const website = await getWebsiteById(websiteId);
|
||||
const website = await getWebsite(websiteId);
|
||||
|
||||
return ok(res, website);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useAuth, useCors, useValidate } from 'lib/middleware';
|
|||
import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getReportsByWebsiteId } from 'queries';
|
||||
import { getWebsiteReports } from 'queries';
|
||||
import { pageInfo } from 'lib/schema';
|
||||
|
||||
export interface ReportsRequestQuery extends SearchFilter {
|
||||
|
@ -35,7 +35,7 @@ export default async (
|
|||
|
||||
const { page, query, pageSize } = req.query;
|
||||
|
||||
const data = await getReportsByWebsiteId(websiteId, {
|
||||
const data = await getWebsiteReports(websiteId, {
|
||||
page,
|
||||
pageSize: +pageSize || undefined,
|
||||
query,
|
||||
|
|
|
@ -1,39 +1,30 @@
|
|||
import { Prisma, Report } from '@prisma/client';
|
||||
import prisma from 'lib/prisma';
|
||||
import { FilterResult, ReportSearchFilter } from 'lib/types';
|
||||
import ReportFindUniqueArgs = Prisma.ReportFindUniqueArgs;
|
||||
import ReportFindManyArgs = Prisma.ReportFindManyArgs;
|
||||
|
||||
export async function createReport(data: Prisma.ReportUncheckedCreateInput): Promise<Report> {
|
||||
return prisma.client.report.create({ data });
|
||||
async function findReport(criteria: ReportFindUniqueArgs) {
|
||||
return prisma.client.report.findUnique(criteria);
|
||||
}
|
||||
|
||||
export async function getReportById(reportId: string): Promise<Report> {
|
||||
return prisma.client.report.findUnique({
|
||||
export async function getReport(reportId: string): Promise<Report> {
|
||||
return findReport({
|
||||
where: {
|
||||
id: reportId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateReport(
|
||||
reportId: string,
|
||||
data: Prisma.ReportUpdateInput,
|
||||
): Promise<Report> {
|
||||
return prisma.client.report.update({ where: { id: reportId }, data });
|
||||
}
|
||||
|
||||
export async function deleteReport(reportId: string): Promise<Report> {
|
||||
return prisma.client.report.delete({ where: { id: reportId } });
|
||||
}
|
||||
|
||||
export async function getReports(
|
||||
params: ReportSearchFilter,
|
||||
options?: { include?: Prisma.ReportInclude },
|
||||
criteria: ReportFindManyArgs,
|
||||
filters: ReportSearchFilter = {},
|
||||
): Promise<FilterResult<Report[]>> {
|
||||
const { query, userId, websiteId } = params;
|
||||
|
||||
const mode = prisma.getQueryMode();
|
||||
const { query, userId, websiteId } = filters;
|
||||
|
||||
const where: Prisma.ReportWhereInput = {
|
||||
...criteria.where,
|
||||
userId,
|
||||
websiteId,
|
||||
AND: [
|
||||
|
@ -93,32 +84,18 @@ export async function getReports(
|
|||
],
|
||||
};
|
||||
|
||||
const [pageFilters, pageInfo] = prisma.getPageFilters(params);
|
||||
|
||||
const reports = await prisma.client.report.findMany({
|
||||
where,
|
||||
...pageFilters,
|
||||
...(options?.include && { include: options.include }),
|
||||
});
|
||||
|
||||
const count = await prisma.client.report.count({
|
||||
where,
|
||||
});
|
||||
|
||||
return {
|
||||
data: reports,
|
||||
count,
|
||||
...pageInfo,
|
||||
};
|
||||
return prisma.pagedQuery('report', { where }, filters);
|
||||
}
|
||||
|
||||
export async function getReportsByUserId(
|
||||
export async function getUserReports(
|
||||
userId: string,
|
||||
filter?: ReportSearchFilter,
|
||||
filters?: ReportSearchFilter,
|
||||
): Promise<FilterResult<Report[]>> {
|
||||
return getReports(
|
||||
{ userId, ...filter },
|
||||
{
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
include: {
|
||||
website: {
|
||||
select: {
|
||||
|
@ -128,12 +105,35 @@ export async function getReportsByUserId(
|
|||
},
|
||||
},
|
||||
},
|
||||
filters,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getReportsByWebsiteId(
|
||||
export async function getWebsiteReports(
|
||||
websiteId: string,
|
||||
filter: ReportSearchFilter,
|
||||
filters: ReportSearchFilter = {},
|
||||
): Promise<FilterResult<Report[]>> {
|
||||
return getReports({ websiteId, ...filter });
|
||||
return getReports(
|
||||
{
|
||||
where: {
|
||||
websiteId,
|
||||
},
|
||||
},
|
||||
filters,
|
||||
);
|
||||
}
|
||||
|
||||
export async function createReport(data: Prisma.ReportUncheckedCreateInput): Promise<Report> {
|
||||
return prisma.client.report.create({ data });
|
||||
}
|
||||
|
||||
export async function updateReport(
|
||||
reportId: string,
|
||||
data: Prisma.ReportUpdateInput,
|
||||
): Promise<Report> {
|
||||
return prisma.client.report.update({ where: { id: reportId }, data });
|
||||
}
|
||||
|
||||
export async function deleteReport(reportId: string): Promise<Report> {
|
||||
return prisma.client.report.delete({ where: { id: reportId } });
|
||||
}
|
||||
|
|
|
@ -3,29 +3,109 @@ import { ROLES } from 'lib/constants';
|
|||
import { uuid } from 'lib/crypto';
|
||||
import prisma from 'lib/prisma';
|
||||
import { FilterResult, TeamSearchFilter } from 'lib/types';
|
||||
import TeamFindManyArgs = Prisma.TeamFindManyArgs;
|
||||
|
||||
export interface GetTeamOptions {
|
||||
includeTeamUser?: boolean;
|
||||
export async function findTeam(criteria: Prisma.TeamFindFirstArgs): Promise<Team> {
|
||||
return prisma.client.team.findFirst(criteria);
|
||||
}
|
||||
|
||||
async function getTeam(where: Prisma.TeamWhereInput, options: GetTeamOptions = {}): Promise<Team> {
|
||||
const { includeTeamUser = false } = options;
|
||||
const { client } = prisma;
|
||||
export async function getTeam(teamId: string, options: { includeMembers?: boolean } = {}) {
|
||||
const { includeMembers } = options;
|
||||
|
||||
return client.team.findFirst({
|
||||
where,
|
||||
include: {
|
||||
teamUser: includeTeamUser,
|
||||
return findTeam({
|
||||
where: {
|
||||
id: teamId,
|
||||
},
|
||||
...(includeMembers && { include: { teamUser: true } }),
|
||||
});
|
||||
}
|
||||
|
||||
export function getTeamById(teamId: string, options: GetTeamOptions = {}) {
|
||||
return getTeam({ id: teamId }, options);
|
||||
export async function getTeams(
|
||||
criteria: TeamFindManyArgs,
|
||||
filters: TeamSearchFilter = {},
|
||||
): Promise<FilterResult<Team[]>> {
|
||||
const mode = prisma.getQueryMode();
|
||||
const { userId, query } = filters;
|
||||
|
||||
const where: Prisma.TeamWhereInput = {
|
||||
...criteria.where,
|
||||
...(userId && {
|
||||
teamUser: {
|
||||
some: { userId },
|
||||
},
|
||||
}),
|
||||
...(query && {
|
||||
AND: {
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
startsWith: query,
|
||||
mode,
|
||||
},
|
||||
},
|
||||
{
|
||||
teamUser: {
|
||||
some: {
|
||||
role: ROLES.teamOwner,
|
||||
user: {
|
||||
username: {
|
||||
startsWith: query,
|
||||
mode,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
return prisma.pagedQuery<TeamFindManyArgs>(
|
||||
'team',
|
||||
{
|
||||
...criteria,
|
||||
where,
|
||||
},
|
||||
filters,
|
||||
);
|
||||
}
|
||||
|
||||
export function getTeamByAccessCode(accessCode: string, options: GetTeamOptions = {}) {
|
||||
return getTeam({ accessCode }, options);
|
||||
export async function getUserTeams(userId: string, filters: TeamSearchFilter = {}) {
|
||||
return getTeams(
|
||||
{
|
||||
where: {
|
||||
teamUser: {
|
||||
some: { userId },
|
||||
},
|
||||
},
|
||||
include: {
|
||||
teamUser: {
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
_count: {
|
||||
select: {
|
||||
website: {
|
||||
where: { deletedAt: null },
|
||||
},
|
||||
teamUser: {
|
||||
where: {
|
||||
user: { deletedAt: null },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
filters,
|
||||
);
|
||||
}
|
||||
|
||||
export async function createTeam(data: Prisma.TeamCreateInput, userId: string): Promise<any> {
|
||||
|
@ -93,94 +173,3 @@ export async function deleteTeam(
|
|||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function getTeams(
|
||||
filters: TeamSearchFilter,
|
||||
options?: { include?: Prisma.TeamInclude },
|
||||
): Promise<FilterResult<Team[]>> {
|
||||
const { userId, query } = filters;
|
||||
const mode = prisma.getQueryMode();
|
||||
const { client } = prisma;
|
||||
|
||||
const where: Prisma.TeamWhereInput = {
|
||||
...(userId && {
|
||||
teamUser: {
|
||||
some: { userId },
|
||||
},
|
||||
}),
|
||||
...(query && {
|
||||
AND: {
|
||||
OR: [
|
||||
{
|
||||
name: { startsWith: query, mode },
|
||||
},
|
||||
{
|
||||
teamUser: {
|
||||
some: {
|
||||
role: ROLES.teamOwner,
|
||||
user: {
|
||||
username: {
|
||||
startsWith: query,
|
||||
mode,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const [pageFilters, getParameters] = prisma.getPageFilters({
|
||||
orderBy: 'name',
|
||||
...filters,
|
||||
});
|
||||
|
||||
const teams = await client.team.findMany({
|
||||
where: {
|
||||
...where,
|
||||
},
|
||||
...pageFilters,
|
||||
...(options?.include && { include: options?.include }),
|
||||
});
|
||||
|
||||
const count = await client.team.count({ where });
|
||||
|
||||
return { data: teams, count, ...getParameters };
|
||||
}
|
||||
|
||||
export async function getTeamsByUserId(
|
||||
userId: string,
|
||||
filter?: TeamSearchFilter,
|
||||
): Promise<FilterResult<Team[]>> {
|
||||
return getTeams(
|
||||
{ userId, ...filter },
|
||||
{
|
||||
include: {
|
||||
teamUser: {
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
_count: {
|
||||
select: {
|
||||
website: {
|
||||
where: { deletedAt: null },
|
||||
},
|
||||
teamUser: {
|
||||
where: {
|
||||
user: { deletedAt: null },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import { Prisma, TeamUser } from '@prisma/client';
|
||||
import { TeamUser } from '@prisma/client';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import prisma from 'lib/prisma';
|
||||
|
||||
export async function getTeamUserById(teamUserId: string): Promise<TeamUser> {
|
||||
return prisma.client.teamUser.findUnique({
|
||||
where: {
|
||||
id: teamUserId,
|
||||
},
|
||||
});
|
||||
}
|
||||
import { FilterResult, TeamUserSearchFilter } from 'lib/types';
|
||||
|
||||
export async function getTeamUser(teamId: string, userId: string): Promise<TeamUser> {
|
||||
return prisma.client.teamUser.findFirst({
|
||||
|
@ -21,20 +14,25 @@ export async function getTeamUser(teamId: string, userId: string): Promise<TeamU
|
|||
|
||||
export async function getTeamUsers(
|
||||
teamId: string,
|
||||
): Promise<(TeamUser & { user: { id: string; username: string } })[]> {
|
||||
return prisma.client.teamUser.findMany({
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
filters?: TeamUserSearchFilter,
|
||||
): Promise<FilterResult<TeamUser[]>> {
|
||||
return prisma.pagedQuery(
|
||||
'teamUser',
|
||||
{
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
filters,
|
||||
);
|
||||
}
|
||||
|
||||
export async function createTeamUser(
|
||||
|
@ -52,18 +50,6 @@ export async function createTeamUser(
|
|||
});
|
||||
}
|
||||
|
||||
export async function updateTeamUser(
|
||||
teamUserId: string,
|
||||
data: Prisma.TeamUserUpdateInput,
|
||||
): Promise<TeamUser> {
|
||||
return prisma.client.teamUser.update({
|
||||
where: {
|
||||
id: teamUserId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteTeamUser(teamId: string, userId: string): Promise<TeamUser> {
|
||||
const { client } = prisma;
|
||||
|
||||
|
@ -74,15 +60,3 @@ export async function deleteTeamUser(teamId: string, userId: string): Promise<Te
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteTeamUserByUserId(
|
||||
userId: string,
|
||||
teamId: string,
|
||||
): Promise<Prisma.BatchPayload> {
|
||||
return prisma.client.teamUser.deleteMany({
|
||||
where: {
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,24 +4,25 @@ import { ROLES } from 'lib/constants';
|
|||
import prisma from 'lib/prisma';
|
||||
import { FilterResult, Role, User, UserSearchFilter } from 'lib/types';
|
||||
import { getRandomChars } from 'next-basics';
|
||||
import UserFindManyArgs = Prisma.UserFindManyArgs;
|
||||
|
||||
export interface GetUserOptions {
|
||||
includePassword?: boolean;
|
||||
showDeleted?: boolean;
|
||||
}
|
||||
|
||||
async function getUser(
|
||||
where: Prisma.UserWhereUniqueInput,
|
||||
async function findUser(
|
||||
criteria: Prisma.UserFindUniqueArgs,
|
||||
options: GetUserOptions = {},
|
||||
): Promise<User> {
|
||||
const { includePassword = false, showDeleted = false } = options;
|
||||
|
||||
if (showDeleted) {
|
||||
where.deletedAt = null;
|
||||
}
|
||||
|
||||
return prisma.client.user.findUnique({
|
||||
where,
|
||||
...criteria,
|
||||
where: {
|
||||
...criteria.where,
|
||||
...(showDeleted && { delatedAt: null }),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
|
@ -32,19 +33,26 @@ async function getUser(
|
|||
});
|
||||
}
|
||||
|
||||
export async function getUserById(id: string, options: GetUserOptions = {}) {
|
||||
return getUser({ id }, options);
|
||||
export async function getUser(userId: string, options: GetUserOptions = {}) {
|
||||
return findUser(
|
||||
{
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getUserByUsername(username: string, options: GetUserOptions = {}) {
|
||||
return getUser({ username }, options);
|
||||
return findUser({ where: { username } }, options);
|
||||
}
|
||||
|
||||
export async function getUsers(
|
||||
params: UserSearchFilter,
|
||||
options?: { include?: Prisma.UserInclude },
|
||||
criteria: UserFindManyArgs,
|
||||
filters?: UserSearchFilter,
|
||||
): Promise<FilterResult<User[]>> {
|
||||
const { teamId, query } = params;
|
||||
const { teamId, query } = filters;
|
||||
const mode = prisma.getQueryMode();
|
||||
|
||||
const where: Prisma.UserWhereInput = {
|
||||
|
@ -67,49 +75,19 @@ export async function getUsers(
|
|||
],
|
||||
},
|
||||
}),
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
const [pageFilters, getParameters] = prisma.getPageFilters({
|
||||
orderBy: 'createdAt',
|
||||
sortDescending: true,
|
||||
...params,
|
||||
});
|
||||
|
||||
const users = await prisma.client.user
|
||||
.findMany({
|
||||
where: {
|
||||
...where,
|
||||
deletedAt: null,
|
||||
},
|
||||
...pageFilters,
|
||||
...(options?.include && { include: options.include }),
|
||||
})
|
||||
.then((a: { [x: string]: any; password: any }[]) => {
|
||||
return a.map(({ password, ...rest }) => rest);
|
||||
});
|
||||
|
||||
const count = await prisma.client.user.count({
|
||||
where: {
|
||||
...where,
|
||||
deletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
return { data: users as any, count, ...getParameters };
|
||||
}
|
||||
|
||||
export async function getUsersByTeamId(teamId: string, filter?: UserSearchFilter) {
|
||||
return getUsers(
|
||||
{ teamId, ...filter },
|
||||
return prisma.pagedQuery(
|
||||
'user',
|
||||
{
|
||||
include: {
|
||||
teamUser: {
|
||||
select: {
|
||||
teamId: true,
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
...criteria,
|
||||
where,
|
||||
},
|
||||
{
|
||||
orderBy: 'createdAt',
|
||||
sortDescending: true,
|
||||
...filters,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,29 +2,37 @@ import { Prisma, Website } from '@prisma/client';
|
|||
import cache from 'lib/cache';
|
||||
import prisma from 'lib/prisma';
|
||||
import { FilterResult, WebsiteSearchFilter } from 'lib/types';
|
||||
import WebsiteFindManyArgs = Prisma.WebsiteFindManyArgs;
|
||||
|
||||
async function getWebsite(where: Prisma.WebsiteWhereUniqueInput): Promise<Website> {
|
||||
return prisma.client.website.findUnique({
|
||||
where,
|
||||
async function findWebsite(criteria: Prisma.WebsiteFindManyArgs): Promise<Website> {
|
||||
return prisma.client.website.findUnique(criteria);
|
||||
}
|
||||
|
||||
export async function getWebsite(websiteId: string) {
|
||||
return findWebsite({
|
||||
where: {
|
||||
id: websiteId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getWebsiteById(id: string) {
|
||||
return getWebsite({ id });
|
||||
}
|
||||
|
||||
export async function getWebsiteByShareId(shareId: string) {
|
||||
return getWebsite({ shareId });
|
||||
export async function getSharedWebsite(shareId: string) {
|
||||
return findWebsite({
|
||||
where: {
|
||||
shareId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getWebsites(
|
||||
criteria: WebsiteFindManyArgs,
|
||||
filters: WebsiteSearchFilter,
|
||||
options?: { include?: Prisma.WebsiteInclude },
|
||||
): Promise<FilterResult<Website[]>> {
|
||||
const { userId, teamId, query } = filters;
|
||||
const mode = prisma.getQueryMode();
|
||||
|
||||
const where: Prisma.WebsiteWhereInput = {
|
||||
...criteria.where,
|
||||
AND: [
|
||||
{
|
||||
OR: [
|
||||
|
@ -47,34 +55,29 @@ export async function getWebsites(
|
|||
: [],
|
||||
},
|
||||
],
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
const [pageFilters, getParameters] = prisma.getPageFilters({
|
||||
orderBy: 'name',
|
||||
...filters,
|
||||
});
|
||||
|
||||
const websites = await prisma.client.website.findMany({
|
||||
where: {
|
||||
...where,
|
||||
deletedAt: null,
|
||||
},
|
||||
...pageFilters,
|
||||
...(options?.include && { include: options.include }),
|
||||
});
|
||||
|
||||
const count = await prisma.client.website.count({ where: { ...where, deletedAt: null } });
|
||||
|
||||
return { data: websites, count, ...getParameters };
|
||||
return prisma.pagedQuery('website', { where }, filters);
|
||||
}
|
||||
|
||||
export async function getWebsitesByUserId(
|
||||
export async function getAllWebsites(userId: string) {
|
||||
return prisma.client.website.findMany({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getUserWebsites(
|
||||
userId: string,
|
||||
filters?: WebsiteSearchFilter,
|
||||
): Promise<FilterResult<Website[]>> {
|
||||
return getWebsites(
|
||||
{ userId, ...filters },
|
||||
{
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
|
@ -84,19 +87,22 @@ export async function getWebsitesByUserId(
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
orderBy: 'name',
|
||||
...filters,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function getWebsitesByTeamId(
|
||||
export async function getTeamWebsites(
|
||||
teamId: string,
|
||||
filters?: WebsiteSearchFilter,
|
||||
): Promise<FilterResult<Website[]>> {
|
||||
return getWebsites(
|
||||
{
|
||||
teamId,
|
||||
...filters,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
|
@ -106,80 +112,10 @@ export async function getWebsitesByTeamId(
|
|||
},
|
||||
},
|
||||
},
|
||||
filters,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getUserWebsites(
|
||||
userId: string,
|
||||
options?: { includeTeams: boolean },
|
||||
): Promise<Website[]> {
|
||||
const { rawQuery } = prisma;
|
||||
|
||||
if (options?.includeTeams) {
|
||||
const websites = await rawQuery(
|
||||
`
|
||||
select
|
||||
website_id as "id",
|
||||
name,
|
||||
domain,
|
||||
share_id as "shareId",
|
||||
reset_at as "resetAt",
|
||||
user_id as "userId",
|
||||
created_at as "createdAt",
|
||||
updated_at as "updatedAt",
|
||||
deleted_at as "deletedAt",
|
||||
null as "teamId",
|
||||
null as "teamName"
|
||||
from website
|
||||
where user_id = {{userId::uuid}}
|
||||
and deleted_at is null
|
||||
union
|
||||
select
|
||||
w.website_id as "id",
|
||||
w.name,
|
||||
w.domain,
|
||||
w.share_id as "shareId",
|
||||
w.reset_at as "resetAt",
|
||||
w.user_id as "userId",
|
||||
w.created_at as "createdAt",
|
||||
w.updated_at as "updatedAt",
|
||||
w.deleted_at as "deletedAt",
|
||||
t.team_id as "teamId",
|
||||
t.name as "teamName"
|
||||
from website w
|
||||
inner join team_website tw
|
||||
on tw.website_id = w.website_id
|
||||
inner join team t
|
||||
on t.team_id = tw.team_id
|
||||
inner join team_user tu
|
||||
on tu.team_id = tw.team_id
|
||||
where tu.user_id = {{userId::uuid}}
|
||||
and w.deleted_at is null
|
||||
`,
|
||||
{ userId },
|
||||
);
|
||||
|
||||
return websites.reduce((arr, item) => {
|
||||
if (!arr.find(({ id }) => id === item.id)) {
|
||||
return arr.concat(item);
|
||||
}
|
||||
return arr;
|
||||
}, []);
|
||||
}
|
||||
|
||||
return prisma.client.website.findMany({
|
||||
where: {
|
||||
userId,
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: [
|
||||
{
|
||||
name: 'asc',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export async function createWebsite(
|
||||
data: Prisma.WebsiteCreateInput | Prisma.WebsiteUncheckedCreateInput,
|
||||
): Promise<Website> {
|
||||
|
|
Loading…
Reference in New Issue
Block a user