diff --git a/public/locales/en/layout/manage.json b/public/locales/en/layout/manage.json index bcb6b8928..1af1c5bf1 100644 --- a/public/locales/en/layout/manage.json +++ b/public/locales/en/layout/manage.json @@ -21,6 +21,12 @@ "discord": "Community Discord", "contribute": "Contribute" } + }, + "tools": { + "title": "Tools", + "items": { + "docker": "Docker" + } } } } \ No newline at end of file diff --git a/public/locales/en/tools/docker.json b/public/locales/en/tools/docker.json new file mode 100644 index 000000000..548af472e --- /dev/null +++ b/public/locales/en/tools/docker.json @@ -0,0 +1,8 @@ +{ + "title": "Docker", + "alerts": { + "notConfigured": { + "text": "Your Homarr instance does not have Docker configured. Please check the documentation on how to set up the integration." + } + } +} \ No newline at end of file diff --git a/src/components/layout/Templates/ManageLayout.tsx b/src/components/layout/Templates/ManageLayout.tsx index 5634ee10a..5cdf063d4 100644 --- a/src/components/layout/Templates/ManageLayout.tsx +++ b/src/components/layout/Templates/ManageLayout.tsx @@ -16,12 +16,14 @@ import { useDisclosure } from '@mantine/hooks'; import { IconBook2, IconBrandDiscord, + IconBrandDocker, IconBrandGithub, IconGitFork, IconHome, IconLayoutDashboard, IconMailForward, IconQuestionMark, + IconTool, IconUser, IconUsers, TablerIconsProps, @@ -173,7 +175,7 @@ const CustomNavigationLink = forwardRef< return ( }> - {Object.entries(navigationLink.items).map(([itemName, item]) => { + {Object.entries(navigationLink.items).map(([itemName, item], index) => { const commonItemProps = { label: t(`navigation.${name}.items.${itemName}`), icon: , @@ -183,10 +185,18 @@ const CustomNavigationLink = forwardRef< const matchesActive = router.pathname.endsWith(item.href); if (item.href.startsWith('http')) { - return ; + return ( + + ); } - return ; + return ; })} ); @@ -223,28 +233,38 @@ const navigationLinks: NavigationLinks = { }, }, }, + tools: { + icon: IconTool, + onlyAdmin: true, + items: { + docker: { + icon: IconBrandDocker, + href: '/manage/tools/docker', + }, + }, + }, help: { icon: IconQuestionMark, items: { documentation: { icon: IconBook2, href: 'https://homarr.dev/docs/about', - target: '_blank' + target: '_blank', }, report: { icon: IconBrandGithub, href: 'https://github.com/ajnart/homarr/issues/new/choose', - target: '_blank' + target: '_blank', }, discord: { icon: IconBrandDiscord, href: 'https://discord.com/invite/aCsmEV5RgA', - target: '_blank' + target: '_blank', }, contribute: { icon: IconGitFork, href: 'https://github.com/ajnart/homarr', - target: '_blank' + target: '_blank', }, }, }, diff --git a/src/pages/docker.tsx b/src/pages/manage/tools/docker.tsx similarity index 52% rename from src/pages/docker.tsx rename to src/pages/manage/tools/docker.tsx index 2dd0700db..6399d06e6 100644 --- a/src/pages/docker.tsx +++ b/src/pages/manage/tools/docker.tsx @@ -1,8 +1,10 @@ -import { Stack } from '@mantine/core'; +import { Alert, Stack, Title } from '@mantine/core'; +import { IconInfoCircle } from '@tabler/icons-react'; import { ContainerInfo } from 'dockerode'; -import { GetServerSideProps } from 'next'; +import { GetServerSideProps, InferGetServerSidePropsType } from 'next'; import { useState } from 'react'; -import { MainLayout } from '~/components/layout/Templates/MainLayout'; +import { useTranslation } from 'next-i18next'; +import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; import { env } from '~/env'; import ContainerActionBar from '~/modules/Docker/ContainerActionBar'; import DockerTable from '~/modules/Docker/DockerTable'; @@ -11,27 +13,45 @@ import { getServerSideTranslations } from '~/tools/server/getServerSideTranslati import { boardNamespaces } from '~/tools/server/translation-namespaces'; import { api } from '~/utils/api'; -export default function DockerPage() { +export default function DockerPage({ + dockerIsConfigured, +}: InferGetServerSidePropsType) { const [selection, setSelection] = useState([]); - const { data, refetch, isRefetching } = api.docker.containers.useQuery(); + const { data, refetch, isRefetching } = api.docker.containers.useQuery(undefined, { + enabled: dockerIsConfigured, + }); + + const { t } = useTranslation('tools/docker'); const reload = () => { refetch(); setSelection([]); }; + if (!dockerIsConfigured) { + return ( + + {t('title')} + } color="blue"> + {t('alerts.notConfigured.text')} + + + ); + } + return ( - + - + ); } export const getServerSideProps: GetServerSideProps = async ({ locale, req, res }) => { - if (!env.DOCKER_HOST || !env.DOCKER_PORT) return { notFound: true }; + const dockerIsConfigured = env.DOCKER_HOST !== undefined; + const session = await getServerAuthSession({ req, res }); if (!session?.user.isAdmin) { return { @@ -39,9 +59,15 @@ export const getServerSideProps: GetServerSideProps = async ({ locale, req, res }; } - const translations = await getServerSideTranslations(boardNamespaces, locale, req, res); + const translations = await getServerSideTranslations( + [...boardNamespaces, 'layout/manage', 'tools/docker'], + locale, + req, + res + ); return { props: { + dockerIsConfigured: dockerIsConfigured, ...translations, }, };