diff --git a/public/locales/en/tools/docker.json b/public/locales/en/tools/docker.json index ff8250b72..95c67f0d8 100644 --- a/public/locales/en/tools/docker.json +++ b/public/locales/en/tools/docker.json @@ -4,5 +4,29 @@ "notConfigured": { "text": "Your Homarr instance does not have Docker configured or it has falied to fetch containers. Please check the documentation on how to set up the integration." } + }, + "modals": { + "selectBoard": { + "title": "Choose a board", + "text": "Choose the board where you want to add the apps for the selected Docker containers.", + "form": { + "board": { + "label": "Board" + }, + "submit": "Add apps" + } + } + }, + "notifications": { + "selectBoard": { + "success": { + "title": "Added apps to board", + "message": "The apps for the selected Docker containers have been added to the board." + }, + "error": { + "title": "Failed to add apps to board", + "message": "The apps for the selected Docker containers could not be added to the board." + } + } } } \ No newline at end of file diff --git a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx index d556602b3..c1b2045db 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx @@ -6,11 +6,12 @@ import { motion } from 'framer-motion'; import { useTranslation } from 'next-i18next'; import { ReactNode } from 'react'; import { v4 as uuidv4 } from 'uuid'; - import { useConfigContext } from '~/config/provider'; import { useConfigStore } from '~/config/store'; import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions'; +import { generateDefaultApp } from '~/tools/shared/app'; import { AppType } from '~/types/app'; + import { CategoryEditModalInnerProps } from '../../../../Wrappers/Category/CategoryEditModal'; import { useStyles } from '../Shared/styles'; @@ -66,7 +67,7 @@ export const AvailableElementTypes = ({ closeModal(modalId); showNotification({ title: t('category.created.title'), - message: t('category.created.message', { name: category.name}), + message: t('category.created.message', { name: category.name }), color: 'teal', }); }); @@ -87,39 +88,8 @@ export const AvailableElementTypes = ({ openContextModalGeneric<{ app: AppType; allowAppNamePropagation: boolean }>({ modal: 'editApp', innerProps: { - app: { - id: uuidv4(), - name: t('app.defaultName'), - url: 'https://homarr.dev', - appearance: { - iconUrl: '/imgs/logo/logo.png', - appNameStatus: 'normal', - appNameFontSize: 16, - positionAppName: 'column', - lineClampAppName: 1, - }, - network: { - enabledStatusChecker: true, - statusCodes: ['200', '301', '302', '304', '307', '308'], - okStatus: [200, 301, 302, 304, 307, 308], - }, - behaviour: { - isOpeningNewTab: true, - externalUrl: 'https://homarr.dev', - }, - - area: { - type: 'wrapper', - properties: { - id: getLowestWrapper()?.id ?? 'default', - }, - }, - shape: {}, - integration: { - type: null, - properties: [], - }, - }, + app: generateDefaultApp(getLowestWrapper()?.id ?? 'default'), + // TODO: Add translation? t('app.defaultName') allowAppNamePropagation: true, }, size: 'xl', @@ -168,4 +138,4 @@ const ElementItem = ({ name, icon, onClick }: ElementItemProps) => { ); -}; \ No newline at end of file +}; diff --git a/src/modules/Docker/ContainerActionBar.tsx b/src/components/Manage/Tools/Docker/ContainerActionBar.tsx similarity index 90% rename from src/modules/Docker/ContainerActionBar.tsx rename to src/components/Manage/Tools/Docker/ContainerActionBar.tsx index 8a3cdc381..cf18de50c 100644 --- a/src/modules/Docker/ContainerActionBar.tsx +++ b/src/components/Manage/Tools/Docker/ContainerActionBar.tsx @@ -4,6 +4,7 @@ import { IconCheck, IconPlayerPlay, IconPlayerStop, + IconPlus, IconRefresh, IconRotateClockwise, IconTrash, @@ -12,6 +13,8 @@ import Dockerode from 'dockerode'; import { useTranslation } from 'next-i18next'; import { RouterInputs, api } from '~/utils/api'; +import { openDockerSelectBoardModal } from './docker-select-board.modal'; + export interface ContainerActionBarProps { selected: Dockerode.ContainerInfo[]; reload: () => void; @@ -86,6 +89,16 @@ export default function ContainerActionBar({ > {t('actionBar.remove.title')} + ); } diff --git a/src/modules/Docker/ContainerState.tsx b/src/components/Manage/Tools/Docker/ContainerState.tsx similarity index 100% rename from src/modules/Docker/ContainerState.tsx rename to src/components/Manage/Tools/Docker/ContainerState.tsx diff --git a/src/modules/Docker/DockerTable.tsx b/src/components/Manage/Tools/Docker/ContainerTable.tsx similarity index 94% rename from src/modules/Docker/DockerTable.tsx rename to src/components/Manage/Tools/Docker/ContainerTable.tsx index f51e1e00c..bd0f76c84 100644 --- a/src/modules/Docker/DockerTable.tsx +++ b/src/components/Manage/Tools/Docker/ContainerTable.tsx @@ -8,13 +8,13 @@ import { TextInput, createStyles, } from '@mantine/core'; -import { useDebouncedValue, useElementSize } from '@mantine/hooks'; +import { useElementSize } from '@mantine/hooks'; import { IconSearch } from '@tabler/icons-react'; import Dockerode, { ContainerInfo } from 'dockerode'; import { useTranslation } from 'next-i18next'; -import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react'; +import { Dispatch, SetStateAction, useMemo, useState } from 'react'; -import { MIN_WIDTH_MOBILE } from '~/constants/constants'; +import { MIN_WIDTH_MOBILE } from '../../../../constants/constants'; import ContainerState from './ContainerState'; const useStyles = createStyles((theme) => ({ @@ -26,7 +26,7 @@ const useStyles = createStyles((theme) => ({ }, })); -export default function DockerTable({ +export default function ContainerTable({ containers, selection, setSelection, @@ -37,7 +37,6 @@ export default function DockerTable({ }) { const { t } = useTranslation('modules/docker'); const [search, setSearch] = useState(''); - const { classes, cx } = useStyles(); const { ref, width } = useElementSize(); const filteredContainers = useMemo( diff --git a/src/components/Manage/Tools/Docker/docker-select-board.modal.tsx b/src/components/Manage/Tools/Docker/docker-select-board.modal.tsx new file mode 100644 index 000000000..2567a7551 --- /dev/null +++ b/src/components/Manage/Tools/Docker/docker-select-board.modal.tsx @@ -0,0 +1,120 @@ +import { Button, Group, Select, Stack, Text, TextInput, Title } from '@mantine/core'; +import { useForm } from '@mantine/form'; +import { ContextModalProps, modals } from '@mantine/modals'; +import { showNotification } from '@mantine/notifications'; +import { IconCheck, IconX } from '@tabler/icons-react'; +import { ContainerInfo } from 'dockerode'; +import { Trans, useTranslation } from 'next-i18next'; +import { z } from 'zod'; +import { api } from '~/utils/api'; +import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; + +const dockerSelectBoardSchema = z.object({ + board: z.string().nonempty(), +}); + +type InnerProps = { + containers: ContainerInfo[]; +}; +type FormType = z.infer; + +export const DockerSelectBoardModal = ({ id, innerProps }: ContextModalProps) => { + const { t } = useTranslation('tools/docker'); + const { mutateAsync, isLoading } = api.boards.addAppsForContainers.useMutation(); + const { i18nZodResolver } = useI18nZodResolver(); + const handleSubmit = async (values: FormType) => { + await mutateAsync( + { + apps: innerProps.containers.map((container) => ({ + name: container.Names.at(0) ?? 'App', + port: container.Ports.at(0)?.PublicPort, + })), + boardName: values.board, + }, + { + onSuccess: () => { + showNotification({ + title: t('notifications.selectBoard.success.title'), + message: t('notifications.selectBoard.success.message'), + icon: , + color: 'green', + }); + + modals.close(id); + }, + onError: () => { + showNotification({ + title: t('notifications.selectBoard.error.title'), + message: t('notifications.selectBoard.error.message'), + icon: , + color: 'red', + }); + }, + } + ); + }; + + const form = useForm({ + initialValues: { + board: '', + }, + validate: i18nZodResolver(dockerSelectBoardSchema), + }); + + const { data: boards } = api.boards.all.useQuery(); + + return ( +
+ + {t('modals.selectBoard.text')} + +