diff --git a/src/components/Board/Customize/Layout/LayoutPreview.tsx b/src/components/Board/Customize/Layout/LayoutPreview.tsx index 5a843fbba..ecae8cc6f 100644 --- a/src/components/Board/Customize/Layout/LayoutPreview.tsx +++ b/src/components/Board/Customize/Layout/LayoutPreview.tsx @@ -1,5 +1,5 @@ import { Flex, Group, Indicator, Paper, Stack, createStyles } from '@mantine/core'; -import { Logo } from '~/components/layout/Logo'; +import { Logo } from '~/components/layout/Common/Logo'; import { createDummyArray } from '~/tools/client/arrays'; type LayoutPreviewProps = { diff --git a/src/components/Dashboard/Tiles/Apps/AppTile.tsx b/src/components/Dashboard/Tiles/Apps/AppTile.tsx index 2610cb9b3..0fbe7ab58 100644 --- a/src/components/Dashboard/Tiles/Apps/AppTile.tsx +++ b/src/components/Dashboard/Tiles/Apps/AppTile.tsx @@ -4,7 +4,7 @@ import { motion } from 'framer-motion'; import Link from 'next/link'; import { AppType } from '../../../../types/app'; -import { useCardStyles } from '../../../layout/useCardStyles'; +import { useCardStyles } from '../../../layout/Common/useCardStyles'; import { useEditModeStore } from '../../Views/useEditModeStore'; import { HomarrCardWrapper } from '../HomarrCardWrapper'; import { BaseTileProps } from '../type'; diff --git a/src/components/Dashboard/Tiles/HomarrCardWrapper.tsx b/src/components/Dashboard/Tiles/HomarrCardWrapper.tsx index 815ca892a..89b456c23 100644 --- a/src/components/Dashboard/Tiles/HomarrCardWrapper.tsx +++ b/src/components/Dashboard/Tiles/HomarrCardWrapper.tsx @@ -1,7 +1,7 @@ import { Card, CardProps } from '@mantine/core'; import { ReactNode } from 'react'; -import { useCardStyles } from '../../layout/useCardStyles'; +import { useCardStyles } from '../../layout/Common/useCardStyles'; import { useEditModeStore } from '../Views/useEditModeStore'; interface HomarrCardWrapperProps extends CardProps { diff --git a/src/components/Dashboard/Wrappers/Category/Category.tsx b/src/components/Dashboard/Wrappers/Category/Category.tsx index ca7dfd469..0819c9b91 100644 --- a/src/components/Dashboard/Wrappers/Category/Category.tsx +++ b/src/components/Dashboard/Wrappers/Category/Category.tsx @@ -16,7 +16,7 @@ import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../../../config/provider'; import { CategoryType } from '../../../../types/category'; -import { useCardStyles } from '../../../layout/useCardStyles'; +import { useCardStyles } from '../../../layout/Common/useCardStyles'; import { useEditModeStore } from '../../Views/useEditModeStore'; import { WrapperContent } from '../WrapperContent'; import { useGridstack } from '../gridstack/use-gridstack'; diff --git a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx index 5e604aa3d..95b955150 100644 --- a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx +++ b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx @@ -1,7 +1,7 @@ import { Card } from '@mantine/core'; import { RefObject } from 'react'; -import { useCardStyles } from '../../../layout/useCardStyles'; +import { useCardStyles } from '../../../layout/Common/useCardStyles'; import { WrapperContent } from '../WrapperContent'; import { useGridstack } from '../gridstack/use-gridstack'; diff --git a/src/components/Settings/Common/CommonSettings.tsx b/src/components/Settings/Common/CommonSettings.tsx deleted file mode 100644 index b72ff76ee..000000000 --- a/src/components/Settings/Common/CommonSettings.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { ScrollArea, Space, Stack, Text } from '@mantine/core'; -import { useViewportSize } from '@mantine/hooks'; - -import { useConfigContext } from '../../../config/provider'; -import ConfigChanger from '../../Config/ConfigChanger'; -import ConfigActions from './Config/ConfigActions'; -import LanguageSelect from './Language/LanguageSelect'; -import { SearchEngineSelector } from './SearchEngine/SearchEngineSelector'; - -export default function CommonSettings() { - const { config } = useConfigContext(); - const { height, width } = useViewportSize(); - - if (!config) { - return ( - - No active config - - ); - } - return ( - - - - - - - - - - ); -} diff --git a/src/components/Settings/Common/Config/ConfigActions.tsx b/src/components/Settings/Common/Config/ConfigActions.tsx deleted file mode 100644 index e3bb2941d..000000000 --- a/src/components/Settings/Common/Config/ConfigActions.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { - ActionIcon, - Alert, - Center, - Flex, - Text, - createStyles, - useMantineTheme, -} from '@mantine/core'; -import { useDisclosure } from '@mantine/hooks'; -import { openConfirmModal } from '@mantine/modals'; -import { showNotification } from '@mantine/notifications'; -import { - IconAlertTriangle, - IconCheck, - IconCopy, - IconDownload, - IconTrash, - IconX, -} from '@tabler/icons-react'; -import fileDownload from 'js-file-download'; -import { Trans, useTranslation } from 'next-i18next'; -import { useRouter } from 'next/router'; -import { api } from '~/utils/api'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; -import Tip from '../../../layout/Tip'; -import { CreateConfigCopyModal } from './CreateCopyModal'; - -export default function ConfigActions() { - const { t } = useTranslation(['settings/general/config-changer', 'settings/common', 'common']); - const [createCopyModalOpened, createCopyModal] = useDisclosure(false); - const { config } = useConfigContext(); - const { mutateAsync } = useDeleteConfigMutation(); - - if (!config) return null; - - const handleDownload = () => { - fileDownload(JSON.stringify(config, null, '\t'), `${config?.configProperties.name}.json`); - }; - - const handleDeletion = async () => { - openConfirmModal({ - title: t('modal.confirmDeletion.title'), - children: ( - <> - } mb="md"> - , code: }} - /> - - {t('modal.confirmDeletion.text')} - - ), - labels: { - confirm: ( - , code: }} - /> - ), - cancel: t('common:cancel'), - }, - zIndex: 201, - onConfirm: async () => { - const response = await mutateAsync({ - name: config?.configProperties.name ?? 'default', - }); - }, - }); - }; - - const { classes } = useStyles(); - const { colors } = useMantineTheme(); - - return ( - <> - - - - - {t('buttons.download')} - - - - {t('buttons.delete.text')} - - - - {t('buttons.saveCopy')} - - - -
- {t('settings/common:tips.configTip')} -
- - ); -} - -const useDeleteConfigMutation = () => { - const { t } = useTranslation(['settings/general/config-changer']); - const router = useRouter(); - const { removeConfig } = useConfigStore(); - - return api.config.delete.useMutation({ - onError(error) { - if (error.data?.code === 'FORBIDDEN') { - showNotification({ - title: t('buttons.delete.notifications.deleteFailedDefaultConfig.title'), - icon: , - color: 'red', - autoClose: 1500, - radius: 'md', - message: t('buttons.delete.notifications.deleteFailedDefaultConfig.message'), - }); - } - showNotification({ - title: t('buttons.delete.notifications.deleteFailed.title'), - icon: , - color: 'red', - autoClose: 1500, - radius: 'md', - message: t('buttons.delete.notifications.deleteFailed.message'), - }); - }, - onSuccess(data, variables) { - showNotification({ - title: t('buttons.delete.notifications.deleted.title'), - icon: , - color: 'green', - autoClose: 1500, - radius: 'md', - message: t('buttons.delete.notifications.deleted.message'), - }); - - removeConfig(variables.name); - - router.push('/'); - }, - }); -}; - -const useStyles = createStyles(() => ({ - actionIcon: { - width: 'auto', - height: 'auto', - maxWidth: 'auto', - maxHeight: 'auto', - flexGrow: 1, - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - textAlign: 'center', - rowGap: 10, - padding: 10, - }, -})); diff --git a/src/components/Settings/Common/Config/CreateCopyModal.tsx b/src/components/Settings/Common/Config/CreateCopyModal.tsx deleted file mode 100644 index 1acdd89cc..000000000 --- a/src/components/Settings/Common/Config/CreateCopyModal.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { Button, Group, Modal, TextInput, Title } from '@mantine/core'; -import { useForm } from '@mantine/form'; -import { showNotification } from '@mantine/notifications'; -import { IconCheck, IconX } from '@tabler/icons-react'; -import { useTranslation } from 'next-i18next'; -import { useConfigContext } from '~/config/provider'; -import { api } from '~/utils/api'; - -import { useConfigStore } from '../../../../config/store'; - -interface CreateConfigCopyModalProps { - opened: boolean; - closeModal: () => void; - initialConfigName: string; -} - -export const CreateConfigCopyModal = ({ - opened, - closeModal, - initialConfigName, -}: CreateConfigCopyModalProps) => { - const { configs } = useConfigStore(); - const { config } = useConfigContext(); - const { t } = useTranslation(['settings/general/config-changer']); - - const form = useForm({ - initialValues: { - configName: initialConfigName, - }, - validate: { - configName: (value) => { - if (!value) { - return t('modal.copy.form.configName.validation.required'); - } - - const configNames = configs.map((x) => x.value.configProperties.name); - if (configNames.includes(value)) { - return t('modal.copy.form.configName.validation.notUnique'); - } - - return undefined; - }, - }, - validateInputOnChange: true, - validateInputOnBlur: true, - }); - - const { mutateAsync } = useCopyConfigMutation(); - - const handleClose = () => { - form.setFieldValue('configName', initialConfigName); - closeModal(); - }; - - const handleSubmit = async (values: typeof form.values) => { - if (!form.isValid) return; - - if (!config) { - throw new Error('config is not defiend'); - } - - const copiedConfig = config; - copiedConfig.configProperties.name = form.values.configName; - - await mutateAsync({ - name: form.values.configName, - config: copiedConfig, - }); - closeModal(); - }; - - return ( - {t('modal.copy.title')}} - > -
- - - - - -
- ); -}; - -const useCopyConfigMutation = () => { - const { t } = useTranslation(['settings/general/config-changer']); - const utils = api.useContext(); - - return api.config.save.useMutation({ - onSuccess(_data, variables) { - showNotification({ - title: t('modal.copy.events.configCopied.title'), - icon: , - color: 'green', - autoClose: 1500, - radius: 'md', - message: t('modal.copy.events.configCopied.message', { configName: variables.name }), - }); - // Invalidate a query to fetch new config - utils.config.all.invalidate(); - }, - onError(_error, variables) { - showNotification({ - title: t('modal.events.configNotCopied.title'), - icon: , - color: 'red', - autoClose: 1500, - radius: 'md', - message: t('modal.events.configNotCopied.message', { configName: variables.name }), - }); - }, - }); -}; diff --git a/src/components/Settings/Customization/CustomizationAccordeon.tsx b/src/components/Settings/Customization/CustomizationAccordeon.tsx deleted file mode 100644 index 33789f50c..000000000 --- a/src/components/Settings/Customization/CustomizationAccordeon.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { Accordion, Checkbox, Grid, Group, Stack, Text } from '@mantine/core'; -import { - IconAccessible, - IconBrush, - IconChartCandle, - IconCode, - IconDragDrop, - IconLayout, -} from '@tabler/icons-react'; -import { i18n, useTranslation } from 'next-i18next'; -import { ReactNode } from 'react'; -import { env } from '~/env'; - -import { AccessibilitySettings } from './Accessibility/AccessibilitySettings'; -import { GridstackConfiguration } from './Layout/GridstackConfiguration'; -import { LayoutSelector } from './Layout/LayoutSelector'; -import { BackgroundChanger } from './Meta/BackgroundChanger'; -import { FaviconChanger } from './Meta/FaviconChanger'; -import { LogoImageChanger } from './Meta/LogoImageChanger'; -import { BrowserTabTitle } from './Meta/MetaTitleChanger'; -import { DashboardTitleChanger } from './Meta/PageTitleChanger'; -import { ColorSelector } from './Theme/ColorSelector'; -import { CustomCssChanger } from './Theme/CustomCssChanger'; -import { DashboardTilesOpacitySelector } from './Theme/OpacitySelector'; -import { ShadeSelector } from './Theme/ShadeSelector'; - -export const CustomizationSettingsAccordeon = () => { - const items = getItems().map((item) => ( - - - - - - {item.content} - - - )); - return ( - - {items} - - ); -}; - -interface AccordionLabelProps { - label: string; - image: ReactNode; - description: string; -} - -const AccordionLabel = ({ label, image, description }: AccordionLabelProps) => ( - - {image} -
- {label} - - {description} - -
-
-); - -const getItems = () => { - const { t } = useTranslation([ - 'settings/customization/general', - 'settings/customization/color-selector', - ]); - const items = [ - { - id: 'layout', - image: , - label: t('accordeon.layout.name'), - description: t('accordeon.layout.description'), - content: , - }, - { - id: 'gridstack', - image: , - label: t('accordeon.gridstack.name'), - description: t('accordeon.gridstack.description'), - content: , - }, - { - id: 'accessibility', - image: , - label: t('accordeon.accessibility.name'), - description: t('accordeon.accessibility.description'), - content: , - }, - { - id: 'page_metadata', - image: , - label: t('accordeon.pageMetadata.name'), - description: t('accordeon.pageMetadata.description'), - content: ( - <> - - - - - - ), - }, - { - id: 'appereance', - image: , - label: t('accordeon.appereance.name'), - description: t('accordeon.appereance.description'), - content: ( - <> - - - - {t('settings/customization/color-selector:colors')} - - - - - - - - - - - - - - - - - ), - }, - ]; - if (env.NEXT_PUBLIC_NODE_ENV === 'development') { - items.push({ - id: 'dev', - image: , - label: 'Developer options', - description: 'Options to help when developing', - content: ( - - - // Change to CI mode language - i18n?.changeLanguage(e.target.checked ? 'cimode' : 'en') - } - /> - - ), - }); - } - return items; -}; diff --git a/src/components/Settings/Customization/CustomizationSettings.tsx b/src/components/Settings/Customization/CustomizationSettings.tsx deleted file mode 100644 index 84d484f20..000000000 --- a/src/components/Settings/Customization/CustomizationSettings.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { ScrollArea, Stack, Text } from '@mantine/core'; -import { useViewportSize } from '@mantine/hooks'; -import { useTranslation } from 'next-i18next'; - -import { CustomizationSettingsAccordeon } from './CustomizationAccordeon'; - -export default function CustomizationSettings() { - const { height } = useViewportSize(); - const { t } = useTranslation('settings/customization/general'); - - return ( - - - {t('text')} - - - - ); -} diff --git a/src/components/Settings/Customization/Layout/GridstackConfiguration.tsx b/src/components/Settings/Customization/Layout/GridstackConfiguration.tsx deleted file mode 100644 index 31a70ce53..000000000 --- a/src/components/Settings/Customization/Layout/GridstackConfiguration.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { Alert, Button, Grid, Input, LoadingOverlay, Slider } from '@mantine/core'; -import { useForm } from '@mantine/form'; -import { IconCheck, IconReload } from '@tabler/icons-react'; -import { useTranslation } from 'next-i18next'; -import { useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; -import { GridstackBreakpoints } from '../../../../constants/gridstack-breakpoints'; -import { sleep } from '../../../../tools/client/time'; -import { GridstackSettingsType } from '../../../../types/settings'; - -export const GridstackConfiguration = () => { - const { t } = useTranslation(['settings/customization/gridstack', 'common']); - const { config, name: configName } = useConfigContext(); - const updateConfig = useConfigStore((x) => x.updateConfig); - - if (!config || !configName) { - return null; - } - - const initialValue = config.settings.customization?.gridstack ?? { - columnCountSmall: 3, - columnCountMedium: 6, - columnCountLarge: 12, - }; - - const form = useForm({ - initialValues: initialValue, - }); - - const [isSaving, setIsSaving] = useState(false); - - const handleSubmit = async (values: GridstackSettingsType) => { - setIsSaving(true); - - await sleep(250); - await updateConfig( - configName, - (previousConfig) => ({ - ...previousConfig, - settings: { - ...previousConfig.settings, - customization: { - ...previousConfig.settings.customization, - gridstack: values, - }, - }, - }), - true, - true - ); - - form.resetDirty(); - setIsSaving(false); - }; - - return ( -
- - - - - - - - - - - {form.isDirty() && ( - - {t('unsavedChanges')} - - )} - - - - - - - - - - ); -}; diff --git a/src/components/Settings/Customization/Layout/LayoutSelector.tsx b/src/components/Settings/Customization/Layout/LayoutSelector.tsx deleted file mode 100644 index f5bbd3c87..000000000 --- a/src/components/Settings/Customization/Layout/LayoutSelector.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { - Checkbox, - Divider, - Flex, - Group, - Indicator, - Paper, - Stack, - Text, - Title, - createStyles, -} from '@mantine/core'; -import { useTranslation } from 'next-i18next'; -import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; -import { createDummyArray } from '../../../../tools/client/arrays'; -import { CustomizationSettingsType } from '../../../../types/settings'; -import { Logo } from '../../../layout/Logo'; - -export const LayoutSelector = () => { - const { classes } = useStyles(); - - const { config, name: configName } = useConfigContext(); - const updateConfig = useConfigStore((x) => x.updateConfig); - - const layoutSettings = config?.settings.customization.layout; - - const [leftSidebar, setLeftSidebar] = useState(layoutSettings?.enabledLeftSidebar ?? true); - const [rightSidebar, setRightSidebar] = useState(layoutSettings?.enabledRightSidebar ?? true); - const [docker, setDocker] = useState(layoutSettings?.enabledDocker ?? false); - const [ping, setPing] = useState(layoutSettings?.enabledPing ?? false); - const [searchBar, setSearchBar] = useState(layoutSettings?.enabledSearchbar ?? false); - const { t } = useTranslation('settings/common'); - - if (!configName || !config) return null; - - const handleChange = ( - key: keyof CustomizationSettingsType['layout'], - event: ChangeEvent, - setState: Dispatch> - ) => { - const value = event.target.checked; - setState(value); - updateConfig( - configName, - (prev) => { - const { layout } = prev.settings.customization; - - layout[key] = value; - - return { - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - layout, - }, - }, - }; - }, - true - ); - }; - - const enabledPing = layoutSettings?.enabledPing ?? false; - - return ( - <> - - {t('layout.preview.title')} - - {t('layout.preview.subtitle')} - - - - - - - - {searchBar && } - {docker && } - - - - - - {leftSidebar && ( - - - {createDummyArray(5).map((item, index) => ( - - ))} - - - )} - - - - {createDummyArray(10).map((item, index) => ( - - ))} - - - - {rightSidebar && ( - - - {createDummyArray(5).map((item, index) => ( - - ))} - - - )} - - - - - handleChange('enabledLeftSidebar', ev, setLeftSidebar)} - /> - handleChange('enabledRightSidebar', ev, setRightSidebar)} - /> - handleChange('enabledSearchbar', ev, setSearchBar)} - /> - handleChange('enabledDocker', ev, setDocker)} - /> - handleChange('enabledPing', ev, setPing)} - /> - - - - ); -}; - -const BaseElement = ({ height, width }: { height: number; width: number }) => ( - ({ - backgroundColor: theme.colorScheme === 'dark' ? theme.colors.gray[8] : theme.colors.gray[1], - })} - h={height} - p={2} - w={width} - /> -); - -const PlaceholderElement = (props: any) => { - const { height, width, hasPing, index } = props; - - if (hasPing) { - return ( - - - - ); - } - - return ; -}; - -const useStyles = createStyles((theme) => ({ - primaryWrapper: { - flexGrow: 2, - }, - secondaryWrapper: { - flexGrow: 1, - maxWidth: 100, - }, -})); diff --git a/src/components/Settings/Customization/Meta/BackgroundChanger.tsx b/src/components/Settings/Customization/Meta/BackgroundChanger.tsx deleted file mode 100644 index 53aaea8f3..000000000 --- a/src/components/Settings/Customization/Meta/BackgroundChanger.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { TextInput } from '@mantine/core'; -import { useTranslation } from 'next-i18next'; -import { ChangeEventHandler, useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; - -export const BackgroundChanger = () => { - const { t } = useTranslation('settings/customization/page-appearance'); - const updateConfig = useConfigStore((x) => x.updateConfig); - const { config, name: configName } = useConfigContext(); - const [backgroundImageUrl, setBackgroundImageUrl] = useState( - config?.settings.customization.backgroundImageUrl - ); - - if (!configName) return null; - - const handleChange: ChangeEventHandler = (ev) => { - const { value } = ev.currentTarget; - const backgroundImageUrl = value.trim().length === 0 ? undefined : value; - setBackgroundImageUrl(backgroundImageUrl); - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - backgroundImageUrl, - }, - }, - })); - }; - - return ( - - ); -}; diff --git a/src/components/Settings/Customization/Meta/FaviconChanger.tsx b/src/components/Settings/Customization/Meta/FaviconChanger.tsx deleted file mode 100644 index 5bb880d26..000000000 --- a/src/components/Settings/Customization/Meta/FaviconChanger.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { TextInput } from '@mantine/core'; -import { useTranslation } from 'next-i18next'; -import { ChangeEventHandler, useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; - -export const FaviconChanger = () => { - const { t } = useTranslation('settings/customization/page-appearance'); - const updateConfig = useConfigStore((x) => x.updateConfig); - const { config, name: configName } = useConfigContext(); - const [faviconUrl, setFaviconUrl] = useState( - config?.settings.customization.faviconUrl ?? '/imgs/favicon/favicon.svg' - ); - - if (!configName) return null; - - const handleChange: ChangeEventHandler = (ev) => { - const { value } = ev.currentTarget; - const faviconUrl = value.trim(); - setFaviconUrl(faviconUrl); - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - faviconUrl, - }, - }, - })); - }; - - return ( - - ); -}; diff --git a/src/components/Settings/Customization/Meta/LogoImageChanger.tsx b/src/components/Settings/Customization/Meta/LogoImageChanger.tsx deleted file mode 100644 index 77074c060..000000000 --- a/src/components/Settings/Customization/Meta/LogoImageChanger.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { TextInput } from '@mantine/core'; -import { useTranslation } from 'next-i18next'; -import { ChangeEventHandler, useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; - -export const LogoImageChanger = () => { - const { t } = useTranslation('settings/customization/page-appearance'); - const updateConfig = useConfigStore((x) => x.updateConfig); - const { config, name: configName } = useConfigContext(); - const [logoImageSrc, setLogoImageSrc] = useState( - config?.settings.customization.logoImageUrl ?? '/imgs/logo/logo.png' - ); - - if (!configName) return null; - - const handleChange: ChangeEventHandler = (ev) => { - const { value } = ev.currentTarget; - const logoImageSrc = value.trim(); - setLogoImageSrc(logoImageSrc); - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - logoImageUrl: logoImageSrc, - }, - }, - })); - }; - - return ( - - ); -}; diff --git a/src/components/Settings/Customization/Meta/MetaTitleChanger.tsx b/src/components/Settings/Customization/Meta/MetaTitleChanger.tsx deleted file mode 100644 index 90af2dd97..000000000 --- a/src/components/Settings/Customization/Meta/MetaTitleChanger.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { TextInput } from '@mantine/core'; -import { useTranslation } from 'next-i18next'; -import { ChangeEventHandler, useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; - -export const BrowserTabTitle = () => { - const { t } = useTranslation('settings/customization/page-appearance'); - const updateConfig = useConfigStore((x) => x.updateConfig); - const { config, name: configName } = useConfigContext(); - const [metaTitle, setMetaTitle] = useState(config?.settings.customization.metaTitle ?? ''); - - if (!configName) return null; - - const handleChange: ChangeEventHandler = (ev) => { - const { value } = ev.currentTarget; - const metaTitle = value.trim(); - setMetaTitle(metaTitle); - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - metaTitle, - }, - }, - })); - }; - - return ( - - ); -}; diff --git a/src/components/Settings/Customization/Meta/PageTitleChanger.tsx b/src/components/Settings/Customization/Meta/PageTitleChanger.tsx deleted file mode 100644 index f7745ca4e..000000000 --- a/src/components/Settings/Customization/Meta/PageTitleChanger.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { TextInput } from '@mantine/core'; -import { useTranslation } from 'next-i18next'; -import { ChangeEventHandler, useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; - -export const DashboardTitleChanger = () => { - const { t } = useTranslation('settings/customization/page-appearance'); - const updateConfig = useConfigStore((x) => x.updateConfig); - const { config, name: configName } = useConfigContext(); - const [pageTitle, setPageTitle] = useState(config?.settings.customization.pageTitle ?? ''); - - if (!configName) return null; - - const handleChange: ChangeEventHandler = (ev) => { - const { value } = ev.currentTarget; - const pageTitle = value.trim(); - setPageTitle(pageTitle); - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - pageTitle, - }, - }, - })); - }; - - return ( - - ); -}; diff --git a/src/components/Settings/Customization/Theme/ColorSelector.tsx b/src/components/Settings/Customization/Theme/ColorSelector.tsx deleted file mode 100644 index c3ae8aae2..000000000 --- a/src/components/Settings/Customization/Theme/ColorSelector.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { - ColorSwatch, - Grid, - Group, - MantineTheme, - Popover, - Text, - useMantineTheme, -} from '@mantine/core'; -import { useDisclosure } from '@mantine/hooks'; -import { useTranslation } from 'next-i18next'; -import { useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; -import { useColorTheme } from '../../../../tools/color'; - -interface ColorControlProps { - defaultValue: MantineTheme['primaryColor'] | undefined; - type: 'primary' | 'secondary'; -} - -export function ColorSelector({ type, defaultValue }: ColorControlProps) { - const { t } = useTranslation('settings/customization/color-selector'); - const { config, name: configName } = useConfigContext(); - const [color, setColor] = - type === 'primary' - ? useState(config?.settings.customization.colors.primary || defaultValue) - : useState(config?.settings.customization.colors.secondary || defaultValue); - const [popoverOpened, popover] = useDisclosure(false); - const { setPrimaryColor, setSecondaryColor } = useColorTheme(); - const updateConfig = useConfigStore((x) => x.updateConfig); - - const theme = useMantineTheme(); - const colors = Object.keys(theme.colors).map((color) => ({ - swatch: theme.colors[color][6], - color, - })); - - if (!color || !configName) return null; - - const handleSelection = (color: MantineTheme['primaryColor']) => { - setColor(color); - if (type === 'primary') setPrimaryColor(color); - else setSecondaryColor(color); - updateConfig(configName, (prev) => { - const { colors } = prev.settings.customization; - colors[type] = color; - return { - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - colors, - }, - }, - }; - }); - }; - - const swatches = colors.map(({ color, swatch }) => ( - - handleSelection(color)} - color={swatch} - size={22} - style={{ cursor: 'pointer' }} - /> - - )); - - return ( - - - - - - - - {swatches} - - - - - {t('suffix', { - color: type[0].toUpperCase() + type.slice(1), - })} - - - ); -} diff --git a/src/components/Settings/Customization/Theme/CustomCssChanger.tsx b/src/components/Settings/Customization/Theme/CustomCssChanger.tsx deleted file mode 100644 index 335c9cebd..000000000 --- a/src/components/Settings/Customization/Theme/CustomCssChanger.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Box, Group, Loader, Stack, Text, createStyles, useMantineTheme } from '@mantine/core'; -import { useDebouncedValue } from '@mantine/hooks'; -import { useTranslation } from 'next-i18next'; -import { highlight, languages } from 'prismjs'; -import 'prismjs/components/prism-css'; -import 'prismjs/themes/prism.css'; -import { useEffect, useState } from 'react'; -import Editor from 'react-simple-code-editor'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; - -export const CustomCssChanger = () => { - const { t } = useTranslation('settings/customization/page-appearance'); - const updateConfig = useConfigStore((x) => x.updateConfig); - const { colorScheme, colors } = useMantineTheme(); - const { config, name: configName } = useConfigContext(); - const [nonDebouncedCustomCSS, setNonDebouncedCustomCSS] = useState( - config?.settings.customization.customCss ?? '' - ); - const [debouncedCustomCSS] = useDebouncedValue(nonDebouncedCustomCSS, 696); - const { classes } = useStyles(); - - if (!configName) return null; - - useEffect(() => { - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - customCss: debouncedCustomCSS, - }, - }, - })); - }, [debouncedCustomCSS]); - - const codeIsDirty = nonDebouncedCustomCSS !== debouncedCustomCSS; - const codeEditorHeight = codeIsDirty ? 250 - 42 : 250; - - return ( - - {t('customCSS.label')} - - {t('customCSS.description')} - -
- setNonDebouncedCustomCSS(code)} - highlight={(code) => highlight(code, languages.extend('css', {}), 'css')} - padding={10} - style={{ - fontFamily: '"Fira code", "Fira Mono", monospace', - fontSize: 12, - minHeight: codeEditorHeight, - }} - /> - {codeIsDirty && ( - - - - {t('customCSS.applying')} - - - )} -
-
- ); -}; - -const useStyles = createStyles(({ colors, colorScheme, radius }) => ({ - codeEditorFooter: { - borderBottomLeftRadius: radius.sm, - borderBottomRightRadius: radius.sm, - backgroundColor: colorScheme === 'dark' ? colors.dark[7] : undefined, - }, - codeEditorRoot: { - borderColor: colorScheme === 'dark' ? colors.dark[4] : colors.gray[4], - borderWidth: 1, - borderStyle: 'solid', - borderRadius: radius.sm, - }, - codeEditor: { - backgroundColor: colorScheme === 'dark' ? colors.dark[6] : 'white', - fontSize: 12, - - '& ::placeholder': { - color: colorScheme === 'dark' ? colors.dark[3] : colors.gray[5], - }, - }, -})); diff --git a/src/components/Settings/Customization/Theme/OpacitySelector.tsx b/src/components/Settings/Customization/Theme/OpacitySelector.tsx deleted file mode 100644 index ef25ef364..000000000 --- a/src/components/Settings/Customization/Theme/OpacitySelector.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { Slider, Stack, Text } from '@mantine/core'; -import { useTranslation } from 'next-i18next'; -import { useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; - -export function DashboardTilesOpacitySelector() { - const { config, name: configName } = useConfigContext(); - const [opacity, setOpacity] = useState(config?.settings.customization.appOpacity || 100); - const { t } = useTranslation('settings/customization/opacity-selector'); - - const updateConfig = useConfigStore((x) => x.updateConfig); - - if (!configName) return null; - - const handleChange = (opacity: number) => { - setOpacity(opacity); - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - appOpacity: opacity, - }, - }, - })); - }; - - return ( - - {t('label')} - - - ); -} - -const MARKS = [ - { value: 10, label: '10' }, - { value: 20, label: '20' }, - { value: 30, label: '30' }, - { value: 40, label: '40' }, - { value: 50, label: '50' }, - { value: 60, label: '60' }, - { value: 70, label: '70' }, - { value: 80, label: '80' }, - { value: 90, label: '90' }, - { value: 100, label: '100' }, -]; diff --git a/src/components/Settings/Customization/Theme/ShadeSelector.tsx b/src/components/Settings/Customization/Theme/ShadeSelector.tsx deleted file mode 100644 index 82453c611..000000000 --- a/src/components/Settings/Customization/Theme/ShadeSelector.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { - ColorSwatch, - Grid, - Group, - MantineTheme, - Popover, - Stack, - Text, - useMantineTheme, -} from '@mantine/core'; -import { useDisclosure } from '@mantine/hooks'; -import { useTranslation } from 'next-i18next'; -import { useState } from 'react'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; -import { useColorTheme } from '../../../../tools/color'; - -export function ShadeSelector() { - const { t } = useTranslation('settings/customization/shade-selector'); - const { config, name: configName } = useConfigContext(); - const [shade, setShade] = useState(config?.settings.customization.colors.shade); - const [popoverOpened, popover] = useDisclosure(false); - const { primaryColor, setPrimaryShade } = useColorTheme(); - - const updateConfig = useConfigStore((x) => x.updateConfig); - - const theme = useMantineTheme(); - const primaryShades = theme.colors[primaryColor].map((s, i) => ({ - swatch: theme.colors[primaryColor][i], - shade: i as MantineTheme['primaryShade'], - })); - - if (shade === undefined || !configName) return null; - - const handleSelection = (shade: MantineTheme['primaryShade']) => { - setPrimaryShade(shade); - setShade(shade); - updateConfig(configName, (prev) => ({ - ...prev, - settings: { - ...prev.settings, - customization: { - ...prev.settings.customization, - colors: { - ...prev.settings.customization.colors, - shade, - }, - }, - }, - })); - }; - - const primarySwatches = primaryShades.map(({ swatch, shade }) => ( - - handleSelection(shade)} - color={swatch} - size={22} - style={{ cursor: 'pointer' }} - /> - - )); - - return ( - - - - - - - - - {primarySwatches} - - - - - {t('label')} - - ); -} diff --git a/src/components/Settings/SettingsDrawer.tsx b/src/components/Settings/SettingsDrawer.tsx deleted file mode 100644 index f2906947b..000000000 --- a/src/components/Settings/SettingsDrawer.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Drawer, Tabs, Title } from '@mantine/core'; -import { useTranslation } from 'next-i18next'; - -import { useConfigContext } from '../../config/provider'; -import { useConfigStore } from '../../config/store'; -import CommonSettings from './Common/CommonSettings'; -import CustomizationSettings from './Customization/CustomizationSettings'; - -function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: string }) { - const { t } = useTranslation('settings/common'); - - return ( - - - {t('tabs.common')} - {t('tabs.customizations')} - - - - - - - - - ); -} - -interface SettingsDrawerProps { - opened: boolean; - closeDrawer: () => void; -} - -export function SettingsDrawer({ - opened, - closeDrawer, - newVersionAvailable, -}: SettingsDrawerProps & { newVersionAvailable: string }) { - const { t } = useTranslation('settings/common'); - const { config, name: configName } = useConfigContext(); - const { updateConfig } = useConfigStore(); - - return ( - {t('title')}} - opened={opened} - onClose={() => { - closeDrawer(); - if (!configName || !config) { - return; - } - - updateConfig(configName, (_) => config, false, true); - }} - transitionProps={{ transition: 'slide-left' }} - > - - - ); -} diff --git a/src/components/Settings/Customization/Accessibility/AccessibilitySettings.tsx b/src/components/User/Preferences/AccessibilitySettings.tsx similarity index 100% rename from src/components/Settings/Customization/Accessibility/AccessibilitySettings.tsx rename to src/components/User/Preferences/AccessibilitySettings.tsx diff --git a/src/components/layout/Background.tsx b/src/components/layout/Background.tsx deleted file mode 100644 index 0f714d1b3..000000000 --- a/src/components/layout/Background.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Global } from '@mantine/core'; - -import { useConfigContext } from '../../config/provider'; - -export function Background() { - const { config } = useConfigContext(); - - if (!config?.settings.customization.backgroundImageUrl) { - return null; - } - - return ( - - ); -} diff --git a/src/components/layout/Logo.tsx b/src/components/layout/Common/Logo.tsx similarity index 94% rename from src/components/layout/Logo.tsx rename to src/components/layout/Common/Logo.tsx index 21a4833d9..f57470864 100644 --- a/src/components/layout/Logo.tsx +++ b/src/components/layout/Common/Logo.tsx @@ -1,7 +1,7 @@ import { Group, Image, Text } from '@mantine/core'; import { useScreenLargerThan } from '~/hooks/useScreenLargerThan'; -import { useConfigContext } from '../../config/provider'; +import { useConfigContext } from '../../../config/provider'; import { usePrimaryGradient } from './useGradient'; interface LogoProps { diff --git a/src/components/layout/useCardStyles.ts b/src/components/layout/Common/useCardStyles.ts similarity index 93% rename from src/components/layout/useCardStyles.ts rename to src/components/layout/Common/useCardStyles.ts index d08fd1aac..d813b6bcc 100644 --- a/src/components/layout/useCardStyles.ts +++ b/src/components/layout/Common/useCardStyles.ts @@ -1,6 +1,6 @@ import { createStyles } from '@mantine/core'; -import { useConfigContext } from '../../config/provider'; +import { useConfigContext } from '../../../config/provider'; export const useCardStyles = (isCategory: boolean) => { const { config } = useConfigContext(); diff --git a/src/components/layout/useGradient.tsx b/src/components/layout/Common/useGradient.tsx similarity index 82% rename from src/components/layout/useGradient.tsx rename to src/components/layout/Common/useGradient.tsx index f63e0e8bd..8675a6447 100644 --- a/src/components/layout/useGradient.tsx +++ b/src/components/layout/Common/useGradient.tsx @@ -1,6 +1,6 @@ import { MantineGradient } from '@mantine/core'; -import { useColorTheme } from '../../tools/color'; +import { useColorTheme } from '../../../tools/color'; export const usePrimaryGradient = (): MantineGradient => { const { primaryColor, secondaryColor } = useColorTheme(); diff --git a/src/components/layout/Meta/BoardHead.tsx b/src/components/layout/Meta/BoardHead.tsx new file mode 100644 index 000000000..f91e338f2 --- /dev/null +++ b/src/components/layout/Meta/BoardHead.tsx @@ -0,0 +1,31 @@ +import Head from 'next/head'; +import React from 'react'; + +import { useConfigContext } from '../../../config/provider'; + +export const BoardHeadOverride = () => { + const { config } = useConfigContext(); + + if (!config) return null; + + const { metaTitle, faviconUrl } = config.settings.customization; + + return ( + + {metaTitle && metaTitle.length > 0 && ( + <> + {metaTitle} + + + )} + + {faviconUrl && faviconUrl.length > 0 && ( + <> + + + + + )} + + ); +}; diff --git a/src/components/layout/common-header.tsx b/src/components/layout/Meta/CommonHead.tsx similarity index 54% rename from src/components/layout/common-header.tsx rename to src/components/layout/Meta/CommonHead.tsx index b8559141f..4651cae36 100644 --- a/src/components/layout/common-header.tsx +++ b/src/components/layout/Meta/CommonHead.tsx @@ -1,13 +1,12 @@ +import { useMantineTheme } from '@mantine/core'; import Head from 'next/head'; -import { ReactNode } from 'react'; -interface CommonHeaderProps { - children?: ReactNode; -} +export const CommonHead = () => { + const { colorScheme } = useMantineTheme(); -export const CommonHeader = ({ children }: CommonHeaderProps) => { return ( + @@ -18,7 +17,10 @@ export const CommonHeader = ({ children }: CommonHeaderProps) => { - {children} + ); }; diff --git a/src/components/layout/Meta/Head.tsx b/src/components/layout/Meta/Head.tsx deleted file mode 100644 index 9a1cda3e4..000000000 --- a/src/components/layout/Meta/Head.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable react/no-invalid-html-attribute */ -import NextHead from 'next/head'; -import React from 'react'; - -import { useConfigContext } from '../../../config/provider'; -import { SafariStatusBarStyle } from './SafariStatusBarStyle'; - -export function Head() { - const { config } = useConfigContext(); - - return ( - - {config?.settings.customization.metaTitle || 'Homarr 🦞'} - - - - - {/* configure apple splash screen & touch icon */} - - - - - - - ); -} diff --git a/src/components/layout/Meta/SafariStatusBarStyle.tsx b/src/components/layout/Meta/SafariStatusBarStyle.tsx deleted file mode 100644 index e712c871a..000000000 --- a/src/components/layout/Meta/SafariStatusBarStyle.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useMantineTheme } from '@mantine/core'; - -export const SafariStatusBarStyle = () => { - const { colorScheme } = useMantineTheme(); - const isDark = colorScheme === 'dark'; - return ( - - ); -}; diff --git a/src/components/layout/Templates/BoardLayout.tsx b/src/components/layout/Templates/BoardLayout.tsx index 30c2ab0cc..d2872416b 100644 --- a/src/components/layout/Templates/BoardLayout.tsx +++ b/src/components/layout/Templates/BoardLayout.tsx @@ -1,4 +1,4 @@ -import { Button, Text, Title, Tooltip, clsx } from '@mantine/core'; +import { Button, Global, Text, Title, Tooltip, clsx } from '@mantine/core'; import { useHotkeys, useWindowEvent } from '@mantine/hooks'; import { openContextModal } from '@mantine/modals'; import { hideNotification, showNotification } from '@mantine/notifications'; @@ -19,8 +19,8 @@ import { useConfigContext } from '~/config/provider'; import { env } from '~/env'; import { api } from '~/utils/api'; -import { Background } from '../Background'; import { HeaderActionButton } from '../Header/ActionButton'; +import { BoardHeadOverride } from '../Meta/BoardHead'; import { MainLayout } from './MainLayout'; type BoardLayoutProps = { @@ -32,7 +32,8 @@ export const BoardLayout = ({ children }: BoardLayoutProps) => { return ( }> - + + {children} @@ -195,3 +196,25 @@ const AddElementButton = () => { ); }; + +const BackgroundImage = () => { + const { config } = useConfigContext(); + + if (!config?.settings.customization.backgroundImageUrl) { + return null; + } + + return ( + + ); +}; diff --git a/src/components/layout/Templates/MainLayout.tsx b/src/components/layout/Templates/MainLayout.tsx index dd1e393f5..35831df84 100644 --- a/src/components/layout/Templates/MainLayout.tsx +++ b/src/components/layout/Templates/MainLayout.tsx @@ -1,7 +1,6 @@ import { AppShell, useMantineTheme } from '@mantine/core'; import { MainHeader } from '../Header/Header'; -import { Head } from '../Meta/Head'; type MainLayoutProps = { headerActions?: React.ReactNode; @@ -21,7 +20,6 @@ export const MainLayout = ({ headerActions, children }: MainLayoutProps) => { header={} className="dashboard-app-shell" > - {children} ); diff --git a/src/components/layout/Templates/ManageLayout.tsx b/src/components/layout/Templates/ManageLayout.tsx index ff32aba6d..63ba3d303 100644 --- a/src/components/layout/Templates/ManageLayout.tsx +++ b/src/components/layout/Templates/ManageLayout.tsx @@ -33,7 +33,6 @@ import { useScreenLargerThan } from '~/hooks/useScreenLargerThan'; import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore'; import { MainHeader } from '../Header/Header'; -import { CommonHeader } from '../common-header'; interface ManageLayoutProps { children: ReactNode; @@ -143,7 +142,6 @@ export const ManageLayout = ({ children }: ManageLayoutProps) => { return ( <> - - - - + - + Login • Homarr - + {t('title')} diff --git a/src/pages/manage/boards/index.tsx b/src/pages/manage/boards/index.tsx index 7d6c5831c..7335100c7 100644 --- a/src/pages/manage/boards/index.tsx +++ b/src/pages/manage/boards/index.tsx @@ -21,9 +21,9 @@ import { IconPlus, IconTrash, } from '@tabler/icons-react'; +import Head from 'next/head'; import Link from 'next/link'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; -import { CommonHeader } from '~/components/layout/common-header'; import { sleep } from '~/tools/client/time'; import { api } from '~/utils/api'; @@ -34,9 +34,9 @@ const BoardsPage = () => { return ( <ManageLayout> - <CommonHeader> + <Head> <title>Boards • Homarr - + Boards diff --git a/src/pages/manage/settings/index.tsx b/src/pages/manage/settings/index.tsx index fe9d1a41f..57bc4a9a7 100644 --- a/src/pages/manage/settings/index.tsx +++ b/src/pages/manage/settings/index.tsx @@ -1,13 +1,13 @@ import { Text, Title } from '@mantine/core'; +import Head from 'next/head'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; -import { CommonHeader } from '~/components/layout/common-header'; const SettingsPage = () => { return ( - + Settings • Homarr - + Settings Coming soon! diff --git a/src/pages/user/preferences.tsx b/src/pages/user/preferences.tsx index 3205d290a..891fef13f 100644 --- a/src/pages/user/preferences.tsx +++ b/src/pages/user/preferences.tsx @@ -2,12 +2,12 @@ import { Button, Group, Select, Stack, Text, Title } from '@mantine/core'; import { createFormContext } from '@mantine/form'; import type { InferGetServerSidePropsType } from 'next'; import { GetServerSidePropsContext } from 'next'; +import Head from 'next/head'; import { forwardRef } from 'react'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { AccessibilitySettings } from '~/components/Settings/Customization/Accessibility/AccessibilitySettings'; +import { AccessibilitySettings } from '~/components/User/Preferences/AccessibilitySettings'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; -import { CommonHeader } from '~/components/layout/common-header'; import { languages } from '~/tools/language'; import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; import { manageNamespaces } from '~/tools/server/translation-namespaces'; @@ -20,9 +20,9 @@ const PreferencesPage = ({ locale }: InferGetServerSidePropsType - + Preferences • Homarr - + Preferences {data && } @@ -141,7 +141,12 @@ const SelectItem = forwardRef( ); export async function getServerSideProps({ req, res, locale }: GetServerSidePropsContext) { - const translations = await getServerSideTranslations(manageNamespaces, locale, undefined, undefined); + const translations = await getServerSideTranslations( + manageNamespaces, + locale, + undefined, + undefined + ); return { props: { ...translations,