Add match by list of integrations

This commit is contained in:
ajnart
2023-07-03 10:47:28 +09:00
parent 8339536596
commit 45bfbb1b48
4 changed files with 278 additions and 176 deletions

View File

@@ -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
}
}
}
}

View File

@@ -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<any>;
}) {
if (!integration.type) return null;
return (
<Accordion.Item value={integration.id}>
<Accordion.Control>{integration.name}</Accordion.Control>
<Accordion.Panel>
<IntegrationOptionsRendererNoForm
type={integration.type}
properties={integration.properties}
/>
<Stack>
<TextInput label="url" {...form.getInputProps(`${integration.type}.${integrationIdx}.url`)} />
{integration.properties.map((property, idx) => {
const test = form.getInputProps(`${integration.type}.${integrationIdx}.properties.${idx}.value`);
if (property.type === 'private')
return (
<PasswordInput
label={property.field}
{...form.getInputProps(`${integration.type}.${integrationIdx}.properties.${idx}.value`)}
/>
);
else if (property.type === 'public')
return (
<TextInput
label={property.field}
{...form.getInputProps(`${integration.type}.${integrationIdx}.properties.${idx}.value`)}
/>
);
})}
</Stack>
</Accordion.Panel>
</Accordion.Item>
);
@@ -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 (
<Accordion variant="separated" multiple>
{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 (
<Accordion.Item
value={integration.label ?? integration.value}
key={configIntegration.type}
>
<Accordion.Item value={integration.type ?? integration.name} key={integration.type}>
<Accordion.Control
icon={
<Image
src={integration.image}
src={image}
withPlaceholder
width={24}
height={24}
alt={integration.label ?? integration.value}
alt={integration.type ?? integration.name}
/>
}
>
{integration.label ?? integration.value}
{integration.name}
</Accordion.Control>
<Accordion.Panel>
<Accordion variant="contained" radius="md" multiple>
{configIntegration.integration.map((item) => (
<IntegrationDisplay integration={item} />
))}
<Accordion variant="separated" radius="md" multiple>
{configIntegrations.map((integration, integrationIdx) => {
return (
<IntegrationDisplay
integrationIdx={integrationIdx}
form={form}
integration={integration}
/>
);
})}
</Accordion>
</Accordion.Panel>
</Accordion.Item>

View File

@@ -51,6 +51,7 @@ export type IntegrationType =
export type AppIntegrationType = {
type: IntegrationType | null;
id: string;
url: string;
name: string;
properties: AppIntegrationPropertyType[];
};

View File

@@ -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<string, any>[];
settings: SettingsType;
integrations: AppIntegrationType[]
integrations: IntegrationTypeMap;
}
export type BackendConfigType = Omit<ConfigType, 'apps'> & {