diff --git a/public/locales/en/settings/general/config-changer.json b/public/locales/en/settings/general/config-changer.json index ccd79607a..130273158 100644 --- a/public/locales/en/settings/general/config-changer.json +++ b/public/locales/en/settings/general/config-changer.json @@ -18,6 +18,14 @@ "configSaved": { "title": "Config saved", "message": "Config saved as {{configName}}" + }, + "configCopied": { + "title": "Config copied", + "message": "Config copied as {{configName}}" + }, + "configNotCopied": { + "title": "Unable to copy config", + "message": "Your config was not copied as {{configName}}" } } }, diff --git a/src/components/Settings/Common/Config/ConfigActions.tsx b/src/components/Settings/Common/Config/ConfigActions.tsx index 1978c9542..46a71b03d 100644 --- a/src/components/Settings/Common/Config/ConfigActions.tsx +++ b/src/components/Settings/Common/Config/ConfigActions.tsx @@ -1,11 +1,10 @@ import { ActionIcon, Center, createStyles, Flex, Text, useMantineTheme } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; -import { showNotification } from '@mantine/notifications'; -import { IconCheck, IconCopy, IconDownload, IconTrash, IconX } from '@tabler/icons'; -import { useMutation } from '@tanstack/react-query'; +import { IconCopy, IconDownload, IconTrash } from '@tabler/icons'; import fileDownload from 'js-file-download'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../../../config/provider'; +import { useDeleteConfigMutation } from '../../../../tools/config/mutations/useDeleteConfigMutation'; import Tip from '../../../layout/Tip'; import { CreateConfigCopyModal } from './CreateCopyModal'; @@ -79,36 +78,3 @@ const useStyles = createStyles(() => ({ padding: 10, }, })); - -const useDeleteConfigMutation = (configName: string) => { - const { t } = useTranslation(['settings/general/config-changer']); - - return useMutation({ - mutationKey: ['config/delete', { configName }], - mutationFn: () => fetchDeletion(configName), - onSuccess() { - showNotification({ - title: t('buttons.delete.notifications.deleted.title'), - icon: , - color: 'green', - autoClose: 1500, - radius: 'md', - message: t('buttons.delete.notifications.deleted.message'), - }); - // TODO: set config to default config and use fallback config if necessary - }, - onError() { - showNotification({ - title: t('buttons.delete.notifications.deleteFailed.title'), - icon: , - color: 'red', - autoClose: 1500, - radius: 'md', - message: t('buttons.delete.notifications.deleteFailed.message'), - }); - }, - }); -}; - -const fetchDeletion = async (configName: string) => - (await fetch(`/api/configs/${configName}`)).json(); diff --git a/src/components/Settings/Common/Config/CreateCopyModal.tsx b/src/components/Settings/Common/Config/CreateCopyModal.tsx index b88ba736d..c5f0b874e 100644 --- a/src/components/Settings/Common/Config/CreateCopyModal.tsx +++ b/src/components/Settings/Common/Config/CreateCopyModal.tsx @@ -1,9 +1,7 @@ import { Button, Group, Modal, TextInput, Title } from '@mantine/core'; import { useForm } from '@mantine/form'; -import { showNotification } from '@mantine/notifications'; -import { IconCheck } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; -import { useConfigContext } from '../../../../config/provider'; +import { useCopyConfigMutation } from '../../../../tools/config/mutations/useCopyConfigMutation'; interface CreateConfigCopyModalProps { opened: boolean; @@ -17,7 +15,7 @@ export const CreateConfigCopyModal = ({ initialConfigName, }: CreateConfigCopyModalProps) => { const { t } = useTranslation(['settings/general/config-changer']); - const { config } = useConfigContext(); + const form = useForm({ initialValues: { configName: initialConfigName, @@ -27,23 +25,18 @@ export const CreateConfigCopyModal = ({ }, }); + const { mutateAsync } = useCopyConfigMutation(form.values.configName); + const handleClose = () => { form.setFieldValue('configName', initialConfigName); closeModal(); }; - const handleSubmit = (values: typeof form.values) => { + const handleSubmit = async (values: typeof form.values) => { if (!form.isValid) return; - // TODO: create config file with copied data + + await mutateAsync(); closeModal(); - showNotification({ - title: t('modal.events.configSaved.title'), - icon: , - color: 'green', - autoClose: 1500, - radius: 'md', - message: t('modal.events.configSaved.message', { configName: values.configName }), - }); }; return ( diff --git a/src/pages/api/configs/[slug].ts b/src/pages/api/configs/[slug].ts index e9f14d3c1..c43309c9f 100644 --- a/src/pages/api/configs/[slug].ts +++ b/src/pages/api/configs/[slug].ts @@ -71,16 +71,20 @@ function Get(req: NextApiRequest, res: NextApiResponse) { message: 'Wrong request', }); } + // Loop over all the files in the /data/configs directory const files = fs.readdirSync('data/configs'); + // Strip the .json extension from the file name const configs = files.map((file) => file.replace('.json', '')); + // If the target is not in the list of files, return an error if (!configs.includes(slug)) { return res.status(404).json({ message: 'Target not found', }); } + // Return the content of the file return res.status(200).json(fs.readFileSync(path.join('data/configs', `${slug}.json`), 'utf8')); } @@ -90,12 +94,15 @@ export default async (req: NextApiRequest, res: NextApiResponse) => { if (req.method === 'PUT') { return Put(req, res); } + if (req.method === 'DELETE') { return Delete(req, res); } + if (req.method === 'GET') { return Get(req, res); } + return res.status(405).json({ statusCode: 405, message: 'Method not allowed', @@ -110,16 +117,20 @@ function Delete(req: NextApiRequest, res: NextApiResponse) { message: 'Wrong request', }); } + // Loop over all the files in the /data/configs directory const files = fs.readdirSync('data/configs'); + // Strip the .json extension from the file name const configs = files.map((file) => file.replace('.json', '')); + // If the target is not in the list of files, return an error if (!configs.includes(slug)) { return res.status(404).json({ message: 'Target not found', }); } + // Delete the file fs.unlinkSync(path.join('data/configs', `${slug}.json`)); return res.status(200).json({ diff --git a/src/pages/api/configs/index.ts b/src/pages/api/configs/index.ts index 78aabc4e8..07abc7507 100644 --- a/src/pages/api/configs/index.ts +++ b/src/pages/api/configs/index.ts @@ -12,12 +12,6 @@ function Get(req: NextApiRequest, res: NextApiResponse) { export default async (req: NextApiRequest, res: NextApiResponse) => { // Filter out if the reuqest is a POST or a GET - if (req.method === 'POST') { - return res.status(405).json({ - statusCode: 405, - message: 'Method not allowed', - }); - } if (req.method === 'GET') { return Get(req, res); } diff --git a/src/tools/config/mutations/useCopyConfigMutation.tsx b/src/tools/config/mutations/useCopyConfigMutation.tsx new file mode 100644 index 000000000..c3af88e56 --- /dev/null +++ b/src/tools/config/mutations/useCopyConfigMutation.tsx @@ -0,0 +1,51 @@ +import { showNotification } from '@mantine/notifications'; +import { IconCheck, IconX } from '@tabler/icons'; +import { useMutation } from '@tanstack/react-query'; +import { useTranslation } from 'next-i18next'; +import { useConfigContext } from '../../../config/provider'; +import { ConfigType } from '../../../types/config'; + +export const useCopyConfigMutation = (configName: string) => { + const { config } = useConfigContext(); + const { t } = useTranslation(['settings/general/config-changer']); + + return useMutation({ + mutationKey: ['configs/copy', { configName }], + mutationFn: () => fetchCopy(configName, config), + onSuccess() { + showNotification({ + title: t('modal.events.configCopied.title'), + icon: , + color: 'green', + autoClose: 1500, + radius: 'md', + message: t('modal.events.configCopied.message', { configName }), + }); + }, + onError() { + showNotification({ + title: t('modal.events.configNotCopied.title'), + icon: , + color: 'red', + autoClose: 1500, + radius: 'md', + message: t('modal.events.configNotCopied.message', { configName }), + }); + }, + }); +}; + +const fetchCopy = async (configName: string, config: ConfigType | undefined) => { + if (!config) { + throw new Error('config is not defiend'); + } + + const response = await fetch(`/api/configs/${configName}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(config), + }); + return response.json(); +}; diff --git a/src/tools/config/mutations/useDeleteConfigMutation.tsx b/src/tools/config/mutations/useDeleteConfigMutation.tsx new file mode 100644 index 000000000..e642e2f13 --- /dev/null +++ b/src/tools/config/mutations/useDeleteConfigMutation.tsx @@ -0,0 +1,37 @@ +import { showNotification } from '@mantine/notifications'; +import { IconCheck, IconX } from '@tabler/icons'; +import { useMutation } from '@tanstack/react-query'; +import { useTranslation } from 'next-i18next'; + +export const useDeleteConfigMutation = (configName: string) => { + const { t } = useTranslation(['settings/general/config-changer']); + + return useMutation({ + mutationKey: ['configs/delete', { configName }], + mutationFn: () => fetchDeletion(configName), + onSuccess() { + showNotification({ + title: t('buttons.delete.notifications.deleted.title'), + icon: , + color: 'green', + autoClose: 1500, + radius: 'md', + message: t('buttons.delete.notifications.deleted.message'), + }); + // TODO: set config to default config and use fallback config if necessary + }, + onError() { + showNotification({ + title: t('buttons.delete.notifications.deleteFailed.title'), + icon: , + color: 'red', + autoClose: 1500, + radius: 'md', + message: t('buttons.delete.notifications.deleteFailed.message'), + }); + }, + }); +}; + +const fetchDeletion = async (configName: string) => + (await fetch(`/api/configs/${configName}`, { method: 'DELETE' })).json();