mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-11 07:55:52 +01:00
✨ Add new types for integration configuration
This commit is contained in:
@@ -12,15 +12,15 @@ import {
|
||||
ThemeIcon,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { IconDeviceFloppy } from '@tabler/icons';
|
||||
import { IconDeviceFloppy, TablerIcon } from '@tabler/icons';
|
||||
import { ReactNode, useState } from 'react';
|
||||
|
||||
interface GenericSecretInputProps {
|
||||
label: string;
|
||||
value: string;
|
||||
secretIsPresent: boolean;
|
||||
unsetIcon: ReactNode;
|
||||
setIcon: ReactNode;
|
||||
unsetIcon: TablerIcon;
|
||||
setIcon: TablerIcon;
|
||||
}
|
||||
|
||||
export const GenericSecretInput = ({
|
||||
@@ -33,13 +33,15 @@ export const GenericSecretInput = ({
|
||||
const { classes } = useStyles();
|
||||
const [dirty, setDirty] = useState(false);
|
||||
|
||||
const IconComponent = secretIsPresent ? setIcon : unsetIcon;
|
||||
|
||||
return (
|
||||
<Card withBorder>
|
||||
<Grid>
|
||||
<Grid.Col className={classes.alignSelfCenter} xs={12} md={6}>
|
||||
<Group spacing="sm">
|
||||
<ThemeIcon color={secretIsPresent ? 'green' : 'red'} variant="light">
|
||||
{secretIsPresent ? setIcon : unsetIcon}
|
||||
<IconComponent size={16} />
|
||||
</ThemeIcon>
|
||||
<Stack spacing={0}>
|
||||
<Title className={classes.subtitle} order={6}>
|
||||
|
||||
@@ -3,7 +3,15 @@ import { Group, Select, SelectItem, Text } from '@mantine/core';
|
||||
import { UseFormReturnType } from '@mantine/form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { forwardRef } from 'react';
|
||||
import { ServiceType } from '../../../../../../../../types/service';
|
||||
import { IntegrationsType } from '../../../../../../../../types/integration';
|
||||
import {
|
||||
IntegrationField,
|
||||
integrationFieldDefinitions,
|
||||
integrationFieldProperties,
|
||||
ServiceIntegrationPropertyType,
|
||||
ServiceIntegrationType,
|
||||
ServiceType,
|
||||
} from '../../../../../../../../types/service';
|
||||
|
||||
interface IntegrationSelectorProps {
|
||||
form: UseFormReturnType<ServiceType, (item: ServiceType) => ServiceType>;
|
||||
@@ -44,7 +52,23 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/overseerr.png',
|
||||
label: 'Overseerr',
|
||||
},
|
||||
];
|
||||
].filter((x) => Object.keys(integrationFieldProperties).includes(x.value));
|
||||
|
||||
const inputProps = form.getInputProps('integration.type');
|
||||
|
||||
const getNewProperties = (value: string | null): ServiceIntegrationPropertyType[] => {
|
||||
if (!value) return [];
|
||||
const requiredProperties = Object.entries(integrationFieldDefinitions).filter(([k, v]) => {
|
||||
const val = integrationFieldProperties[value as ServiceIntegrationType['type']];
|
||||
return val.includes(k as IntegrationField);
|
||||
})!;
|
||||
return requiredProperties.map(([k, value]) => ({
|
||||
type: value.type,
|
||||
field: k as IntegrationField,
|
||||
value: undefined,
|
||||
isDefined: false,
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -58,8 +82,22 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => {
|
||||
clearable
|
||||
variant="default"
|
||||
mb="md"
|
||||
icon={form.values.integration?.type && <img src={data.find(x => x.value === form.values.integration?.type)?.image} alt="test" width={20} height={20} />}
|
||||
{...form.getInputProps('integration.type')}
|
||||
icon={
|
||||
form.values.integration?.type && (
|
||||
<img
|
||||
src={data.find((x) => x.value === form.values.integration?.type)?.image}
|
||||
alt="test"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{...inputProps}
|
||||
onChange={(value) => {
|
||||
form.setFieldValue('integration.properties', getNewProperties(value));
|
||||
console.log(`changed to value ${value}`);
|
||||
inputProps.onChange(value);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -3,9 +3,9 @@ import { UseFormReturnType } from '@mantine/form';
|
||||
import { IconKey, IconKeyOff, IconLock, IconLockOff, IconUser, IconUserOff } from '@tabler/icons';
|
||||
import {
|
||||
IntegrationField,
|
||||
IntegrationFieldDefinitionType,
|
||||
integrationFieldDefinitions,
|
||||
integrationFieldProperties,
|
||||
ServiceIntegrationPropertyType,
|
||||
ServiceType,
|
||||
} from '../../../../../../../../types/service';
|
||||
import { GenericSecretInput } from '../InputElements/GenericSecretInput';
|
||||
@@ -14,27 +14,6 @@ interface IntegrationOptionsRendererProps {
|
||||
form: UseFormReturnType<ServiceType, (values: ServiceType) => ServiceType>;
|
||||
}
|
||||
|
||||
const secretMappings = [
|
||||
{
|
||||
label: 'username',
|
||||
prettyName: 'Username',
|
||||
icon: <IconUser size={18} />,
|
||||
iconUnset: <IconUserOff size={18} />,
|
||||
},
|
||||
{
|
||||
label: 'password',
|
||||
prettyName: 'Password',
|
||||
icon: <IconLock size={18} />,
|
||||
iconUnset: <IconLockOff size={18} />,
|
||||
},
|
||||
{
|
||||
label: 'apiKey',
|
||||
prettyName: 'API Key',
|
||||
icon: <IconKey size={18} />,
|
||||
iconUnset: <IconKeyOff size={18} />,
|
||||
},
|
||||
];
|
||||
|
||||
export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererProps) => {
|
||||
const selectedIntegration = form.values.integration?.type;
|
||||
|
||||
@@ -44,31 +23,49 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP
|
||||
|
||||
return (
|
||||
<Stack spacing="xs" mb="md">
|
||||
{displayedProperties.map((property) => {
|
||||
const mapping = Object.entries(integrationFieldDefinitions).find(
|
||||
([key, value]) => key as IntegrationField === property
|
||||
);
|
||||
const isPresent = entry[1] !== undefined;
|
||||
{displayedProperties.map((property, index) => {
|
||||
const [_, definition] = Object.entries(integrationFieldDefinitions).find(
|
||||
([key]) => property === key
|
||||
)!;
|
||||
|
||||
if (!mapping) {
|
||||
let indexInFormValue =
|
||||
form.values.integration?.properties.findIndex((p) => p.field === property) ?? -1;
|
||||
if (indexInFormValue === -1) {
|
||||
const type = Object.entries(integrationFieldDefinitions).find(
|
||||
([k, v]) => k === property
|
||||
)![1].type;
|
||||
const newProperty: ServiceIntegrationPropertyType = {
|
||||
type,
|
||||
field: property as IntegrationField,
|
||||
isDefined: false,
|
||||
};
|
||||
form.insertListItem('integration.properties', newProperty);
|
||||
indexInFormValue = form.values.integration!.properties.length;
|
||||
}
|
||||
const formValue = form.values.integration?.properties[indexInFormValue];
|
||||
|
||||
const isPresent = formValue?.isDefined;
|
||||
|
||||
if (!definition) {
|
||||
return (
|
||||
<GenericSecretInput
|
||||
label={`${entry[0]} (potentionally unmapped)`}
|
||||
value={entry[1]}
|
||||
label={`${property} (potentionally unmapped)`}
|
||||
secretIsPresent={isPresent}
|
||||
setIcon={<IconKey size={18} />}
|
||||
unsetIcon={<IconKeyOff size={18} />}
|
||||
setIcon={IconKey}
|
||||
unsetIcon={IconKeyOff}
|
||||
{...form.getInputProps(`integration.properties.${index}.value`)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GenericSecretInput
|
||||
label={mapping.prettyName}
|
||||
value={entry[1]}
|
||||
label={definition.label}
|
||||
value=""
|
||||
secretIsPresent={isPresent}
|
||||
setIcon={mapping.icon}
|
||||
unsetIcon={mapping.iconUnset}
|
||||
setIcon={definition.icon}
|
||||
unsetIcon={definition.iconUnset}
|
||||
{...form.getInputProps(`integration.properties.${index}.value`)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user