Changed route ids to be more explicit.

This commit is contained in:
Mike Cao 2024-01-29 14:47:52 -08:00
parent 1a70350936
commit 18e36aa7b3
105 changed files with 86 additions and 76 deletions

View file

@ -0,0 +1,98 @@
import { Website } from '@prisma/client';
import {
Form,
FormRow,
FormButtons,
Flexbox,
TextField,
Button,
Toggle,
LoadingButton,
useToasts,
} from 'react-basics';
import { useState } from 'react';
import { getRandomChars } from 'next-basics';
import { useApi, useMessages } from 'components/hooks';
const generateId = () => getRandomChars(16);
export function ShareUrl({
website,
hostUrl,
onSave,
}: {
website: Website;
hostUrl?: string;
onSave?: () => void;
}) {
const { domain, shareId } = website;
const { formatMessage, labels, messages } = useMessages();
const [id, setId] = useState(shareId);
const { showToast } = useToasts();
const { post, useMutation } = useApi();
const { mutate, error, isPending } = useMutation({
mutationFn: (data: any) => post(`/websites/${website.id}`, data),
});
const url = `${hostUrl || process.env.hostUrl || window?.location.origin}${
process.env.basePath
}/share/${id}/${encodeURIComponent(domain)}`;
const handleGenerate = () => {
setId(generateId());
};
const handleCheck = (checked: boolean) => {
const data = { shareId: checked ? generateId() : null };
mutate(data, {
onSuccess: async () => {
onSave?.();
showToast({ message: formatMessage(messages.saved), variant: 'success' });
},
});
setId(data.shareId);
};
const handleSave = () => {
mutate(
{ shareId: id },
{
onSuccess: async () => {
showToast({ message: formatMessage(messages.saved), variant: 'success' });
onSave?.();
},
},
);
};
return (
<>
<Toggle checked={Boolean(id)} onChecked={handleCheck} style={{ marginBottom: 30 }}>
{formatMessage(labels.enableShareUrl)}
</Toggle>
{id && (
<Form error={error}>
<FormRow>
<p>{formatMessage(messages.shareUrl)}</p>
<Flexbox gap={10}>
<TextField value={url} readOnly allowCopy />
<Button onClick={handleGenerate}>{formatMessage(labels.regenerate)}</Button>
</Flexbox>
</FormRow>
<FormButtons>
<LoadingButton
variant="primary"
disabled={id === shareId}
isLoading={isPending}
onClick={handleSave}
>
{formatMessage(labels.save)}
</LoadingButton>
</FormButtons>
</Form>
)}
</>
);
}
export default ShareUrl;

View file

@ -0,0 +1,29 @@
import { TextArea } from 'react-basics';
import { useMessages, useConfig } from 'components/hooks';
const SCRIPT_NAME = 'script.js';
export function TrackingCode({ websiteId, hostUrl }: { websiteId: string; hostUrl?: string }) {
const { formatMessage, messages } = useMessages();
const config = useConfig();
const trackerScriptName =
config?.trackerScriptName?.split(',')?.map((n: string) => n.trim())?.[0] || SCRIPT_NAME;
const url = trackerScriptName?.startsWith('http')
? trackerScriptName
: `${hostUrl || process.env.hostUrl || window?.location.origin}${
process.env.basePath
}/${trackerScriptName}`;
const code = `<script defer src="${url}" data-website-id="${websiteId}"></script>`;
return (
<>
<p>{formatMessage(messages.trackingCode)}</p>
<TextArea rows={4} value={code} readOnly allowCopy />
</>
);
}
export default TrackingCode;

View file

@ -0,0 +1,55 @@
import { Button, Modal, ModalTrigger, ActionForm, useToasts } from 'react-basics';
import { useRouter } from 'next/navigation';
import { useMessages } from 'components/hooks';
import WebsiteDeleteForm from './WebsiteDeleteForm';
import WebsiteResetForm from './WebsiteResetForm';
import { touch } from 'store/cache';
export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?: () => void }) {
const { formatMessage, labels, messages } = useMessages();
const router = useRouter();
const { showToast } = useToasts();
const handleReset = async () => {
showToast({ message: formatMessage(messages.saved), variant: 'success' });
onSave?.();
};
const handleDelete = async () => {
touch('websites');
router.push('/settings/websites');
};
return (
<>
<ActionForm
label={formatMessage(labels.resetWebsite)}
description={formatMessage(messages.resetWebsiteWarning)}
>
<ModalTrigger>
<Button variant="secondary">{formatMessage(labels.reset)}</Button>
<Modal title={formatMessage(labels.resetWebsite)}>
{(close: () => void) => (
<WebsiteResetForm websiteId={websiteId} onSave={handleReset} onClose={close} />
)}
</Modal>
</ModalTrigger>
</ActionForm>
<ActionForm
label={formatMessage(labels.deleteWebsite)}
description={formatMessage(messages.deleteWebsiteWarning)}
>
<ModalTrigger>
<Button variant="danger">{formatMessage(labels.delete)}</Button>
<Modal title={formatMessage(labels.deleteWebsite)}>
{(close: () => void) => (
<WebsiteDeleteForm websiteId={websiteId} onSave={handleDelete} onClose={close} />
)}
</Modal>
</ModalTrigger>
</ActionForm>
</>
);
}
export default WebsiteData;

View file

@ -0,0 +1,43 @@
import { useApi, useMessages } from 'components/hooks';
import TypeConfirmationForm from 'components/common/TypeConfirmationForm';
const CONFIRM_VALUE = 'DELETE';
export function WebsiteDeleteForm({
websiteId,
onSave,
onClose,
}: {
websiteId: string;
onSave?: () => void;
onClose?: () => void;
}) {
const { formatMessage, labels } = useMessages();
const { del, useMutation } = useApi();
const { mutate, isPending, error } = useMutation({
mutationFn: (data: any) => del(`/websites/${websiteId}`, data),
});
const handleConfirm = async () => {
mutate(null, {
onSuccess: async () => {
onSave?.();
onClose?.();
},
});
};
return (
<TypeConfirmationForm
confirmationValue={CONFIRM_VALUE}
onConfirm={handleConfirm}
onClose={onClose}
isLoading={isPending}
error={error}
buttonLabel={formatMessage(labels.delete)}
buttonVariant="danger"
/>
);
}
export default WebsiteDeleteForm;

View file

@ -0,0 +1,71 @@
import { Website } from '@prisma/client';
import { useRef } from 'react';
import {
SubmitButton,
Form,
FormInput,
FormRow,
FormButtons,
TextField,
useToasts,
} from 'react-basics';
import { useApi, useMessages } from 'components/hooks';
import { DOMAIN_REGEX } from 'lib/constants';
export function WebsiteEditForm({
website,
onSave,
}: {
website: Website;
onSave?: (data: any) => void;
}) {
const { formatMessage, labels, messages } = useMessages();
const { post, useMutation } = useApi();
const { mutate, error } = useMutation({
mutationFn: (data: any) => post(`/websites/${website.id}`, data),
});
const ref = useRef(null);
const { showToast } = useToasts();
const handleSubmit = async (data: any) => {
mutate(data, {
onSuccess: async () => {
showToast({ message: formatMessage(messages.saved), variant: 'success' });
ref.current.reset(data);
onSave?.(data);
},
});
};
return (
<Form ref={ref} onSubmit={handleSubmit} error={error} values={website}>
<FormRow label={formatMessage(labels.websiteId)}>
<TextField value={website.id} readOnly allowCopy />
</FormRow>
<FormRow label={formatMessage(labels.name)}>
<FormInput name="name" rules={{ required: formatMessage(labels.required) }}>
<TextField />
</FormInput>
</FormRow>
<FormRow label={formatMessage(labels.domain)}>
<FormInput
name="domain"
rules={{
required: formatMessage(labels.required),
pattern: {
value: DOMAIN_REGEX,
message: formatMessage(messages.invalidDomain),
},
}}
>
<TextField />
</FormInput>
</FormRow>
<FormButtons>
<SubmitButton variant="primary">{formatMessage(labels.save)}</SubmitButton>
</FormButtons>
</Form>
);
}
export default WebsiteEditForm;

View file

@ -0,0 +1,42 @@
import { useApi, useMessages } from 'components/hooks';
import TypeConfirmationForm from 'components/common/TypeConfirmationForm';
const CONFIRM_VALUE = 'RESET';
export function WebsiteResetForm({
websiteId,
onSave,
onClose,
}: {
websiteId: string;
onSave?: () => void;
onClose?: () => void;
}) {
const { formatMessage, labels } = useMessages();
const { post, useMutation } = useApi();
const { mutate, isPending, error } = useMutation({
mutationFn: (data: any) => post(`/websites/${websiteId}/reset`, data),
});
const handleConfirm = async () => {
mutate(null, {
onSuccess: async () => {
onSave?.();
onClose?.();
},
});
};
return (
<TypeConfirmationForm
confirmationValue={CONFIRM_VALUE}
onConfirm={handleConfirm}
onClose={onClose}
isLoading={isPending}
error={error}
buttonLabel={formatMessage(labels.reset)}
/>
);
}
export default WebsiteResetForm;

View file

@ -0,0 +1,9 @@
import WebsiteSettings from '../WebsiteSettings';
export default async function WebsiteSettingsPage({ params: { id } }) {
if (process.env.cloudMode) {
return null;
}
return <WebsiteSettings websiteId={id} />;
}