diff --git a/data/configs/default.json b/data/configs/default.json index d690977bb..a5f133c55 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -22,6 +22,67 @@ } ], "apps": [ + { + "id": "5df743d9-5cb1-457c-85d2-64ff86855652", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "statusCodes": [ + "200" + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 1 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 1 + }, + "size": { + "width": 2, + "height": 1 + } + } + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", "name": "Discord", @@ -83,6 +144,76 @@ } } }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", + "name": "Contribute", + "url": "https://github.com/ajnart/homarr", + "behaviour": { + "onClickUrl": "https://github.com/ajnart/homarr", + "externalUrl": "https://github.com/ajnart/homarr", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "statusCodes": [] + }, + "appearance": { + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/github.png" + }, + "integration": { + "type": "transmission", + "properties": [ + { + "field": "username", + "type": "public", + "value": "qwdqwd" + }, + { + "field": "password", + "type": "private", + "value": "qdwqdqw" + } + ] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 2, + "y": 0 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 2 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "lg": { + "location": { + "x": 4, + "y": 0 + }, + "size": { + "width": 2, + "height": 2 + } + } + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", "name": "Donate", @@ -143,126 +274,6 @@ } } } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", - "name": "Contribute", - "url": "https://github.com/ajnart/homarr", - "behaviour": { - "onClickUrl": "https://github.com/ajnart/homarr", - "externalUrl": "https://github.com/ajnart/homarr", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "statusCodes": [] - }, - "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/github.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 2, - "y": 0 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 2 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "lg": { - "location": { - "x": 4, - "y": 0 - }, - "size": { - "width": 2, - "height": 2 - } - } - } - }, - { - "id": "5df743d9-5cb1-457c-85d2-64ff86855652", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "statusCodes": [ - "200" - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 1 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 0, - "y": 1 - }, - "size": { - "width": 2, - "height": 1 - } - } - } } ], "widgets": [ @@ -362,6 +373,74 @@ } } ], + "integrations": { + "sonarr": [ + { + "id": "971aa859-8570-49a1-8d34-dd5c7b3638d1", + "url": "https://discord.com/invite/aCsmEV5RgA", + "name": "Sonarr 4k", + "type": "sonarr", + "properties": [ + { + "field": "apiKey", + "type": "public", + "value": "blabla" + } + ] + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a326", + "url": "https://discord.com/invite/aCsmEV5RgA", + "name": "Sonarr HD", + "type": "sonarr", + "properties": [ + { + "field": "apiKey", + "type": "private", + "value": "blabla" + } + ] + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", + "url": "https://discord.com/invite/aCsmEV5RgA", + "name": "Sonarr 16k", + "type": "sonarr", + "properties": [ + { + "field": "apiKey", + "type": "private", + "value": "blabla" + }, + { + "field": "apiKey 2", + "type": "private", + "value": "blabla 2" + } + ] + } + ], + "transmission": [ + { + "id": "test", + "url": "https://discord.com/invite/aCsmEV5RgA", + "name": "Transmission Porn 4k asian babes", + "type": "transmission", + "properties": [ + { + "field": "username", + "type": "public", + "value": "qwdqwd" + }, + { + "field": "password", + "type": "private", + "value": "qdwqdqw" + } + ] + } + ] + }, "settings": { "common": { "searchEngine": { @@ -378,7 +457,7 @@ "enabledSearchbar": true }, "pageTitle": "Homarr v0.12 ⭐️", - "logoImageUrl": "/imgs/logo/logo.png", + "logoImageUrl": "", "faviconUrl": "/imgs/favicon/favicon-squared.png", "backgroundImageUrl": "", "customCss": "", @@ -390,4 +469,4 @@ "appOpacity": 100 } } -} +} \ No newline at end of file diff --git a/src/components/Config/Integration/IntegrationModal.tsx b/src/components/Config/Integration/IntegrationModal.tsx index 3d76ad32b..0c6ee696c 100644 --- a/src/components/Config/Integration/IntegrationModal.tsx +++ b/src/components/Config/Integration/IntegrationModal.tsx @@ -1,31 +1,26 @@ import { Accordion, + Image, Loader, Menu, Modal, PasswordInput, Stack, Text, + TextInput, Title, rem, } from '@mantine/core'; -import { Form, useForm } from '@mantine/form'; -import { modals, openModal } from '@mantine/modals'; +import { UseFormReturnType, useForm } from '@mantine/form'; import { notifications } from '@mantine/notifications'; import { IconPlugConnected } from '@tabler/icons-react'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useQueryClient } from '@tanstack/react-query'; import { getQueryKey } from '@trpc/react-query'; -import { getCookie, getCookies, setCookie } from 'cookies-next'; -import Image from 'next/image'; -import { useState } from 'react'; +import { getCookie, setCookie } from 'cookies-next'; import { useTranslation } from 'react-i18next'; import { integrationsList } from '~/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector'; -import { - IntegrationOptionsRenderer, - IntegrationOptionsRendererNoForm, -} from '~/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer'; -import { useConfigContext } from '~/config/provider'; -import { AppIntegrationType, AppType, IntegrationType } from '~/types/app'; +import { AppIntegrationType, IntegrationType } from '~/types/app'; +import { IntegrationTypeMap } from '~/types/config'; import { api } from '~/utils/api'; const ModalTitle = ({ title, description }: { title: string; description: string }) => ( @@ -84,16 +79,41 @@ export function IntegrationMenu({ integrationsModal }: { integrationsModal: any ); } -function IntegrationDisplay({ integration }: { integration: AppIntegrationType }) { +function IntegrationDisplay({ + integration, + integrationIdx, + form, +}: { + integration: AppIntegrationType; + integrationIdx: number; + form: UseFormReturnType; +}) { if (!integration.type) return null; + return ( {integration.name} - + + + {integration.properties.map((property, idx) => { + const test = form.getInputProps(`${integration.type}.${integrationIdx}.properties.${idx}.value`); + if (property.type === 'private') + return ( + + ); + else if (property.type === 'public') + return ( + + ); + })} + ); @@ -120,63 +140,61 @@ interface IntegrationGroupedType { // | 'nzbGet' // | 'pihole' // | 'adGuardHome'; + +export interface IntegrationObject { + [key: string]: AppIntegrationType; +} + export function IntegrationsAccordion() { const cookie = getCookie('INTEGRATIONS_PASSWORD'); const queryClient = useQueryClient(); const queryKey = getQueryKey(api.system.checkLogin, { password: cookie?.toString() }, 'query'); - let integrations: AppIntegrationType[] | undefined = queryClient.getQueryData(queryKey); + let integrations: IntegrationTypeMap | undefined = queryClient.getQueryData(queryKey); if (!integrations) { - integrations = []; + return null; } - // Fill configIntegrationList with config.integrations in the - const configIntegrationList: IntegrationGroupedType[] = []; - integrations.forEach((configIntegration) => { - const existingIntegration = configIntegrationList.find( - (integration) => integration.type === configIntegration.type - ); - if (existingIntegration) { - existingIntegration.integration.push(configIntegration); - } else { - configIntegrationList.push({ - type: configIntegration.type!, - integration: [configIntegration], - }); - } + const form = useForm({ + initialValues: integrations, }); + // Loop over integrations item return ( - {configIntegrationList.map((configIntegration) => { - // Match configIntegration with integrationsList - const integration = integrationsList.find( - (integration) => integration.value === configIntegration.type - ); - if (!integration) { - return null; - } + {Object.keys(integrations).map((item) => { + if (!integrations) return null; + const configIntegrations = integrations[item as keyof IntegrationTypeMap]; + console.log('CONFIG INTEGRATIONS', configIntegrations); + const image: string | undefined = integrationsList.find( + (i) => i.value === configIntegrations[0].type + )?.image; + const integration = configIntegrations[0]; return ( - + } > - {integration.label ?? integration.value} + {integration.name} - - {configIntegration.integration.map((item) => ( - - ))} + + {configIntegrations.map((integration, integrationIdx) => { + return ( + + ); + })} diff --git a/src/types/app.ts b/src/types/app.ts index 5e27fe377..9f4e2055c 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -51,6 +51,7 @@ export type IntegrationType = export type AppIntegrationType = { type: IntegrationType | null; id: string; + url: string; name: string; properties: AppIntegrationPropertyType[]; }; diff --git a/src/types/config.ts b/src/types/config.ts index 2805f81ac..b6de4903a 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,9 +1,13 @@ import { IWidget } from '../widgets/widgets'; -import { AppIntegrationType, AppType, ConfigAppType } from './app'; +import { AppIntegrationType, AppType, ConfigAppType, IntegrationType } from './app'; import { CategoryType } from './category'; import { SettingsType } from './settings'; import { WrapperType } from './wrapper'; +export type IntegrationTypeMap = { + [key in IntegrationType]: AppIntegrationType[]; +}; + export interface ConfigType { schemaVersion: number; configProperties: ConfigPropertiesType; @@ -12,7 +16,7 @@ export interface ConfigType { apps: AppType[]; widgets: IWidget[]; settings: SettingsType; - integrations: AppIntegrationType[] + integrations: IntegrationTypeMap; } export type BackendConfigType = Omit & {