mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Added website and team providers.
This commit is contained in:
parent
dbb3801e66
commit
cc273092d5
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
import ConfirmationForm from 'components/common/ConfirmationForm';
|
||||
|
||||
export function ReportDeleteButton({
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
Button,
|
||||
SubmitButton,
|
||||
} from 'react-basics';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
|
||||
export function TeamAddForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
import TypeConfirmationForm from 'components/common/TypeConfirmationForm';
|
||||
|
||||
const CONFIRM_VALUE = 'DELETE';
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
SubmitButton,
|
||||
} from 'react-basics';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
|
||||
export function TeamJoinForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) {
|
||||
const { formatMessage, labels, getMessage } = useMessages();
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
import ConfirmationForm from 'components/common/ConfirmationForm';
|
||||
|
||||
export function TeamLeaveForm({
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
|
||||
export function TeamMemberRemoveButton({
|
||||
teamId,
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Button, Icon, Text, Modal, Icons, ModalTrigger, useToasts } from 'react-basics';
|
||||
import UserAddForm from './UserAddForm';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
|
||||
export function UserAddButton({ onSave }: { onSave?: () => void }) {
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import ConfirmationForm from 'components/common/ConfirmationForm';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
|
||||
export function UserDeleteForm({ userId, username, onSave, onClose }) {
|
||||
const { FormattedMessage, messages, labels, formatMessage } = useMessages();
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
|
||||
import WebsiteAddForm from './WebsiteAddForm';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
|
||||
export function WebsiteAddButton({ teamId, onSave }: { teamId: string; onSave?: () => void }) {
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
|
@ -1,5 +1,4 @@
|
||||
'use client';
|
||||
import { Website } from '@prisma/client';
|
||||
import {
|
||||
Form,
|
||||
FormRow,
|
||||
@ -11,21 +10,22 @@ import {
|
||||
LoadingButton,
|
||||
useToasts,
|
||||
} from 'react-basics';
|
||||
import { useState } from 'react';
|
||||
import { useContext, useState } from 'react';
|
||||
import { getRandomChars } from 'next-basics';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { WebsiteContext } from 'app/(main)/websites/[websiteId]/WebsiteProvider';
|
||||
import { touch } from 'store/modified';
|
||||
|
||||
const generateId = () => getRandomChars(16);
|
||||
|
||||
export function ShareUrl({
|
||||
website,
|
||||
hostUrl,
|
||||
onSave,
|
||||
}: {
|
||||
website: Website;
|
||||
websiteId: string;
|
||||
hostUrl?: string;
|
||||
onSave?: () => void;
|
||||
}) {
|
||||
const website = useContext(WebsiteContext);
|
||||
const { domain, shareId } = website;
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
const [id, setId] = useState(shareId);
|
||||
@ -47,7 +47,6 @@ export function ShareUrl({
|
||||
const data = { shareId: checked ? generateId() : null };
|
||||
mutate(data, {
|
||||
onSuccess: async () => {
|
||||
onSave?.();
|
||||
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||
},
|
||||
});
|
||||
@ -60,7 +59,7 @@ export function ShareUrl({
|
||||
{
|
||||
onSuccess: async () => {
|
||||
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||
onSave?.();
|
||||
touch(`website:${website?.id}`);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
@ -4,7 +4,7 @@ import { useRouter } from 'next/navigation';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import WebsiteDeleteForm from './WebsiteDeleteForm';
|
||||
import WebsiteResetForm from './WebsiteResetForm';
|
||||
import { touch } from 'store/cache';
|
||||
import { touch } from 'store/modified';
|
||||
|
||||
export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?: () => void }) {
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
|
@ -1,6 +1,5 @@
|
||||
'use client';
|
||||
import { Website } from '@prisma/client';
|
||||
import { useRef } from 'react';
|
||||
import { useContext, useRef } from 'react';
|
||||
import {
|
||||
SubmitButton,
|
||||
Form,
|
||||
@ -12,18 +11,20 @@ import {
|
||||
} from 'react-basics';
|
||||
import { useApi, useMessages } from 'components/hooks';
|
||||
import { DOMAIN_REGEX } from 'lib/constants';
|
||||
import { touch } from 'store/modified';
|
||||
import { WebsiteContext } from 'app/(main)/websites/[websiteId]/WebsiteProvider';
|
||||
|
||||
export function WebsiteEditForm({
|
||||
website,
|
||||
onSave,
|
||||
websiteId,
|
||||
}: {
|
||||
website: Website;
|
||||
websiteId: string;
|
||||
onSave?: (data: any) => void;
|
||||
}) {
|
||||
const website = useContext(WebsiteContext);
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
const { post, useMutation } = useApi();
|
||||
const { mutate, error } = useMutation({
|
||||
mutationFn: (data: any) => post(`/websites/${website.id}`, data),
|
||||
mutationFn: (data: any) => post(`/websites/${websiteId}`, data),
|
||||
});
|
||||
const ref = useRef(null);
|
||||
const { showToast } = useToasts();
|
||||
@ -33,7 +34,7 @@ export function WebsiteEditForm({
|
||||
onSuccess: async () => {
|
||||
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||
ref.current.reset(data);
|
||||
onSave?.(data);
|
||||
touch(`website:${website?.id}`);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -41,7 +42,7 @@ export function WebsiteEditForm({
|
||||
return (
|
||||
<Form ref={ref} onSubmit={handleSubmit} error={error} values={website}>
|
||||
<FormRow label={formatMessage(labels.websiteId)}>
|
||||
<TextField value={website.id} readOnly allowCopy />
|
||||
<TextField value={website?.id} readOnly allowCopy />
|
||||
</FormRow>
|
||||
<FormRow label={formatMessage(labels.name)}>
|
||||
<FormInput name="name" rules={{ required: formatMessage(labels.required) }}>
|
||||
|
@ -1,31 +1,23 @@
|
||||
'use client';
|
||||
import { useState, Key } from 'react';
|
||||
import { Item, Tabs, Button, Text, Icon, Loading } from 'react-basics';
|
||||
import { useState, Key, useContext } from 'react';
|
||||
import { Item, Tabs, Button, Text, Icon } from 'react-basics';
|
||||
import Link from 'next/link';
|
||||
import Icons from 'components/icons';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import WebsiteContext from 'app/(main)/websites/[websiteId]/WebsiteContext';
|
||||
import WebsiteEditForm from './[websiteId]/WebsiteEditForm';
|
||||
import WebsiteData from './[websiteId]/WebsiteData';
|
||||
import TrackingCode from './[websiteId]/TrackingCode';
|
||||
import ShareUrl from './[websiteId]/ShareUrl';
|
||||
import { useWebsite, useMessages } from 'components/hooks';
|
||||
import WebsiteEditForm from './WebsiteEditForm';
|
||||
import WebsiteData from './WebsiteData';
|
||||
import TrackingCode from './TrackingCode';
|
||||
import ShareUrl from './ShareUrl';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import { WebsiteContext } from 'app/(main)/websites/[websiteId]/WebsiteProvider';
|
||||
|
||||
export function WebsiteSettings({ websiteId, openExternal = false }) {
|
||||
const website = useContext(WebsiteContext);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { data: website, isLoading, refetch } = useWebsite(websiteId);
|
||||
const [tab, setTab] = useState<Key>('details');
|
||||
|
||||
const handleSave = () => {
|
||||
refetch();
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading position="page" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<WebsiteContext.Provider value={website}>
|
||||
<>
|
||||
<PageHeader title={website?.name} icon={<Icons.Globe />}>
|
||||
<Link href={`/websites/${websiteId}`} target={openExternal ? '_blank' : null}>
|
||||
<Button variant="primary">
|
||||
@ -42,11 +34,11 @@ export function WebsiteSettings({ websiteId, openExternal = false }) {
|
||||
<Item key="share">{formatMessage(labels.shareUrl)}</Item>
|
||||
<Item key="data">{formatMessage(labels.data)}</Item>
|
||||
</Tabs>
|
||||
{tab === 'details' && <WebsiteEditForm website={website} onSave={handleSave} />}
|
||||
{tab === 'details' && <WebsiteEditForm websiteId={websiteId} />}
|
||||
{tab === 'tracking' && <TrackingCode websiteId={websiteId} />}
|
||||
{tab === 'share' && <ShareUrl website={website} onSave={handleSave} />}
|
||||
{tab === 'share' && <ShareUrl websiteId={websiteId} />}
|
||||
{tab === 'data' && <WebsiteData websiteId={websiteId} />}
|
||||
</WebsiteContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
import WebsiteSettings from '../WebsiteSettings';
|
||||
import WebsiteProvider from 'app/(main)/websites/[websiteId]/WebsiteProvider';
|
||||
import WebsiteSettings from './WebsiteSettings';
|
||||
|
||||
export default async function WebsiteSettingsPage({ params: { websiteId } }) {
|
||||
return <WebsiteSettings websiteId={websiteId} />;
|
||||
return (
|
||||
<WebsiteProvider websiteId={websiteId}>
|
||||
<WebsiteSettings websiteId={websiteId} />
|
||||
</WebsiteProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
'use client';
|
||||
import { useTeam, useTeamUrl } from 'components/hooks';
|
||||
import { Loading } from 'react-basics';
|
||||
import TeamContext from './TeamContext';
|
||||
|
||||
export function Team({ children }) {
|
||||
const { teamId } = useTeamUrl();
|
||||
const { data: team, isLoading } = useTeam(teamId);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (!team) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <TeamContext.Provider value={team}>{children}</TeamContext.Provider>;
|
||||
}
|
||||
|
||||
export default Team;
|
@ -1,6 +0,0 @@
|
||||
'use client';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const TeamContext = createContext(null);
|
||||
|
||||
export default TeamContext;
|
30
src/app/(main)/teams/[teamId]/TeamProvider.tsx
Normal file
30
src/app/(main)/teams/[teamId]/TeamProvider.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
'use client';
|
||||
import { createContext, ReactNode, useEffect } from 'react';
|
||||
import { useTeam } from 'components/hooks';
|
||||
import { Loading } from 'react-basics';
|
||||
import useModified from 'store/modified';
|
||||
|
||||
export const TeamContext = createContext(null);
|
||||
|
||||
export function TeamProvider({ teamId, children }: { teamId: string; children: ReactNode }) {
|
||||
const modified = useModified(state => state?.[`team:${teamId}`]);
|
||||
const { data: team, isLoading, isFetching, refetch } = useTeam(teamId);
|
||||
|
||||
useEffect(() => {
|
||||
if (modified) {
|
||||
refetch();
|
||||
}
|
||||
}, [modified]);
|
||||
|
||||
if (isFetching && isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (!team) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <TeamContext.Provider value={team}>{children}</TeamContext.Provider>;
|
||||
}
|
||||
|
||||
export default TeamProvider;
|
@ -1,5 +1,5 @@
|
||||
import Team from './Team';
|
||||
import TeamProvider from './TeamProvider';
|
||||
|
||||
export default function ({ children }) {
|
||||
return <Team>{children}</Team>;
|
||||
return <TeamProvider>{children}</TeamProvider>;
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
'use client';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const WebsiteContext = createContext(null);
|
||||
|
||||
export default WebsiteContext;
|
32
src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx
Normal file
32
src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
'use client';
|
||||
import { createContext, ReactNode, useEffect } from 'react';
|
||||
import { useWebsite } from 'components/hooks';
|
||||
import { Loading } from 'react-basics';
|
||||
import useModified from 'store/modified';
|
||||
|
||||
export const WebsiteContext = createContext(null);
|
||||
|
||||
export function WebsiteProvider({
|
||||
websiteId,
|
||||
children,
|
||||
}: {
|
||||
websiteId: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const modified = useModified(state => state?.[`website:${websiteId}`]);
|
||||
const { data: website, isFetching, isLoading, refetch } = useWebsite(websiteId);
|
||||
|
||||
useEffect(() => {
|
||||
if (modified) {
|
||||
refetch();
|
||||
}
|
||||
}, [modified]);
|
||||
|
||||
if (isFetching && isLoading) {
|
||||
return <Loading position="page" />;
|
||||
}
|
||||
|
||||
return <WebsiteContext.Provider value={website}>{children}</WebsiteContext.Provider>;
|
||||
}
|
||||
|
||||
export default WebsiteProvider;
|
@ -5,7 +5,7 @@ export function useWebsite(websiteId: string, options?: { [key: string]: any })
|
||||
const { get, useQuery } = useApi();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['websites', { websiteId }],
|
||||
queryKey: ['website', { websiteId }],
|
||||
queryFn: () => get(`/websites/${websiteId}`),
|
||||
enabled: !!websiteId,
|
||||
...options,
|
||||
|
@ -22,13 +22,16 @@ export * from 'app/(main)/settings/websites/[websiteId]/TrackingCode';
|
||||
export * from 'app/(main)/settings/websites/[websiteId]/WebsiteDeleteForm';
|
||||
export * from 'app/(main)/settings/websites/[websiteId]/WebsiteEditForm';
|
||||
export * from 'app/(main)/settings/websites/[websiteId]/WebsiteResetForm';
|
||||
export * from 'app/(main)/settings/websites/[websiteId]/WebsiteSettings';
|
||||
|
||||
export * from 'app/(main)/settings/websites/WebsiteAddForm';
|
||||
export * from 'app/(main)/settings/websites/WebsitesHeader';
|
||||
export * from 'app/(main)/settings/websites/WebsiteSettings';
|
||||
export * from 'app/(main)/settings/websites/WebsitesDataTable';
|
||||
export * from 'app/(main)/settings/websites/WebsitesTable';
|
||||
|
||||
export * from 'app/(main)/teams/[teamId]/TeamProvider';
|
||||
export * from 'app/(main)/websites/[websiteId]/WebsiteProvider';
|
||||
|
||||
export * from 'components/common/TypeConfirmationForm';
|
||||
export * from 'components/common/DataTable';
|
||||
export * from 'components/common/Empty';
|
||||
|
@ -57,17 +57,17 @@ export default async (
|
||||
|
||||
const { name, domain, shareId } = req.body;
|
||||
|
||||
let website;
|
||||
|
||||
try {
|
||||
website = await updateWebsite(websiteId, { name, domain, shareId });
|
||||
const website = await updateWebsite(websiteId, { name, domain, shareId });
|
||||
|
||||
return ok(res, website);
|
||||
} catch (e: any) {
|
||||
if (e.message.includes('Unique constraint') && e.message.includes('share_id')) {
|
||||
return serverError(res, 'That share ID is already taken.');
|
||||
}
|
||||
}
|
||||
|
||||
return ok(res, website);
|
||||
return serverError(res, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
|
@ -124,7 +124,7 @@ export async function updateWebsite(
|
||||
): Promise<Website> {
|
||||
return prisma.client.website.update({
|
||||
where: {
|
||||
websiteId,
|
||||
id: websiteId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
@ -146,7 +146,7 @@ export async function resetWebsite(
|
||||
where: { websiteId },
|
||||
}),
|
||||
client.website.update({
|
||||
where: { websiteId },
|
||||
where: { id: websiteId },
|
||||
data: {
|
||||
resetAt: new Date(),
|
||||
},
|
||||
@ -186,10 +186,10 @@ export async function deleteWebsite(
|
||||
data: {
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
where: { websiteId },
|
||||
where: { id: websiteId },
|
||||
})
|
||||
: client.website.delete({
|
||||
where: { websiteId },
|
||||
where: { id: websiteId },
|
||||
}),
|
||||
]).then(async data => {
|
||||
if (cache.enabled) {
|
||||
|
@ -6,8 +6,4 @@ export function setValue(key: string, value: any) {
|
||||
store.setState({ [key]: value });
|
||||
}
|
||||
|
||||
export function touch(key: string) {
|
||||
setValue(key, Date.now());
|
||||
}
|
||||
|
||||
export default store;
|
||||
|
Loading…
Reference in New Issue
Block a user