mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
✨ Remove unset icon, use local form instead of API
This commit is contained in:
@@ -7,80 +7,75 @@ import {
|
|||||||
Grid,
|
Grid,
|
||||||
Group,
|
Group,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
|
||||||
TextInput,
|
TextInput,
|
||||||
ThemeIcon,
|
ThemeIcon,
|
||||||
Title,
|
Title,
|
||||||
|
Tooltip,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconDeviceFloppy, TablerIcon } from '@tabler/icons';
|
import { IconDeviceFloppy, TablerIcon } from '@tabler/icons';
|
||||||
import { ReactNode, useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
interface GenericSecretInputProps {
|
interface GenericSecretInputProps {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
secretIsPresent: boolean;
|
|
||||||
unsetIcon: TablerIcon;
|
|
||||||
setIcon: TablerIcon;
|
setIcon: TablerIcon;
|
||||||
|
onClickUpdateButton: (value: string | undefined) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GenericSecretInput = ({
|
export const GenericSecretInput = ({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
secretIsPresent,
|
|
||||||
setIcon,
|
setIcon,
|
||||||
unsetIcon,
|
onClickUpdateButton,
|
||||||
}: GenericSecretInputProps) => {
|
}: GenericSecretInputProps) => {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const [dirty, setDirty] = useState(false);
|
|
||||||
|
|
||||||
const IconComponent = secretIsPresent ? setIcon : unsetIcon;
|
const Icon = setIcon;
|
||||||
|
|
||||||
|
const [fieldValue, setFieldValue] = useState<string>(value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card withBorder>
|
<Card withBorder>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col className={classes.alignSelfCenter} xs={12} md={6}>
|
<Grid.Col className={classes.alignSelfCenter} xs={12} md={6}>
|
||||||
<Group spacing="sm">
|
<Group spacing="sm">
|
||||||
<ThemeIcon color={secretIsPresent ? 'green' : 'red'} variant="light">
|
<ThemeIcon color="green" variant="light">
|
||||||
<IconComponent size={16} />
|
<Icon size={18} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Stack spacing={0}>
|
<Stack spacing={0}>
|
||||||
<Title className={classes.subtitle} order={6}>
|
<Title className={classes.subtitle} order={6}>
|
||||||
{label}
|
{label}
|
||||||
</Title>
|
</Title>
|
||||||
<Text size="xs" color="dimmed">
|
|
||||||
{secretIsPresent
|
|
||||||
? 'Secret is defined in the configuration'
|
|
||||||
: 'Secret has not been defined'}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col xs={12} md={6}>
|
<Grid.Col xs={12} md={6}>
|
||||||
<Flex gap={10} justify="end" align="end">
|
<Flex gap={10} justify="end" align="end">
|
||||||
{secretIsPresent ? (
|
<Button
|
||||||
<>
|
onClick={() => {
|
||||||
<Button variant="subtle" color="gray" px="xl">
|
setFieldValue('');
|
||||||
Clear Secret
|
onClickUpdateButton(undefined);
|
||||||
</Button>
|
}}
|
||||||
<TextInput
|
variant="subtle"
|
||||||
type="password"
|
color="gray"
|
||||||
placeholder="Leave empty"
|
px="xl"
|
||||||
description={`Update secret${dirty ? ' (unsaved)' : ''}`}
|
>
|
||||||
rightSection={
|
Clear Secret
|
||||||
<ActionIcon disabled={!dirty}>
|
</Button>
|
||||||
<IconDeviceFloppy size={18} />
|
<TextInput
|
||||||
</ActionIcon>
|
onChange={(event) => setFieldValue(event.currentTarget.value)}
|
||||||
}
|
rightSection={
|
||||||
defaultValue={value}
|
<Tooltip label="Update this secret" withinPortal>
|
||||||
onChange={() => setDirty(true)}
|
<ActionIcon onClick={() => onClickUpdateButton(fieldValue)}>
|
||||||
withAsterisk
|
<IconDeviceFloppy width={20} strokeWidth={1.2} />
|
||||||
/>
|
</ActionIcon>
|
||||||
</>
|
</Tooltip>
|
||||||
) : (
|
}
|
||||||
<Button variant="light" px="xl">
|
value={fieldValue}
|
||||||
Define secret
|
type="password"
|
||||||
</Button>
|
placeholder="no value is set"
|
||||||
)}
|
withAsterisk
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Stack } from '@mantine/core';
|
import { Stack } from '@mantine/core';
|
||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { IconKey, IconKeyOff } from '@tabler/icons';
|
import { IconKey } from '@tabler/icons';
|
||||||
import {
|
import {
|
||||||
IntegrationField,
|
IntegrationField,
|
||||||
integrationFieldDefinitions,
|
integrationFieldDefinitions,
|
||||||
@@ -49,11 +49,13 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP
|
|||||||
if (!definition) {
|
if (!definition) {
|
||||||
return (
|
return (
|
||||||
<GenericSecretInput
|
<GenericSecretInput
|
||||||
|
onClickUpdateButton={(value) => {
|
||||||
|
form.setFieldValue(`integration.properties.${index}.value`, value);
|
||||||
|
}}
|
||||||
key={`input-${property}`}
|
key={`input-${property}`}
|
||||||
label={`${property} (potentionally unmapped)`}
|
label={`${property} (potentionally unmapped)`}
|
||||||
secretIsPresent={isPresent}
|
secretIsPresent={isPresent}
|
||||||
setIcon={IconKey}
|
setIcon={IconKey}
|
||||||
unsetIcon={IconKeyOff}
|
|
||||||
{...form.getInputProps(`integration.properties.${index}.value`)}
|
{...form.getInputProps(`integration.properties.${index}.value`)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -61,12 +63,14 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericSecretInput
|
<GenericSecretInput
|
||||||
|
onClickUpdateButton={(value) => {
|
||||||
|
form.setFieldValue(`integration.properties.${index}.value`, value);
|
||||||
|
}}
|
||||||
key={`input-${definition.label}`}
|
key={`input-${definition.label}`}
|
||||||
label={definition.label}
|
label={definition.label}
|
||||||
value=""
|
value=""
|
||||||
secretIsPresent={isPresent}
|
secretIsPresent={isPresent}
|
||||||
setIcon={definition.icon}
|
setIcon={definition.icon}
|
||||||
unsetIcon={definition.iconUnset}
|
|
||||||
{...form.getInputProps(`integration.properties.${index}.value`)}
|
{...form.getInputProps(`integration.properties.${index}.value`)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ export const IntegrationTab = ({ form }: IntegrationTabProps) => {
|
|||||||
{hasIntegrationSelected && (
|
{hasIntegrationSelected && (
|
||||||
<>
|
<>
|
||||||
<Divider label="Integration Configuration" labelPosition="center" mt="xl" mb="md" />
|
<Divider label="Integration Configuration" labelPosition="center" mt="xl" mb="md" />
|
||||||
|
<Text size="sm" color="dimmed" mb="lg">
|
||||||
|
To update a secret, enter a value and click the save button. To remove a secret, use the
|
||||||
|
clear button.
|
||||||
|
</Text>
|
||||||
<IntegrationOptionsRenderer form={form} />
|
<IntegrationOptionsRenderer form={form} />
|
||||||
<Alert icon={<IconAlertTriangle />} color="yellow">
|
<Alert icon={<IconAlertTriangle />} color="yellow">
|
||||||
<Text>
|
<Text>
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { IconKey, IconPassword, IconUser, TablerIcon } from '@tabler/icons';
|
||||||
IconKey,
|
|
||||||
IconKeyOff,
|
|
||||||
IconLockOff,
|
|
||||||
IconPassword,
|
|
||||||
IconUser,
|
|
||||||
IconUserOff,
|
|
||||||
TablerIcon,
|
|
||||||
} from '@tabler/icons';
|
|
||||||
import { TileBaseType } from './tile';
|
import { TileBaseType } from './tile';
|
||||||
|
|
||||||
export interface ServiceType extends TileBaseType {
|
export interface ServiceType extends TileBaseType {
|
||||||
@@ -62,7 +54,7 @@ export type ConfigServiceIntegrationType = Omit<ServiceIntegrationType, 'propert
|
|||||||
export type ServiceIntegrationPropertyType = {
|
export type ServiceIntegrationPropertyType = {
|
||||||
type: 'private' | 'public';
|
type: 'private' | 'public';
|
||||||
field: IntegrationField;
|
field: IntegrationField;
|
||||||
value?: string | null;
|
value?: string | undefined;
|
||||||
isDefined: boolean;
|
isDefined: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,7 +81,6 @@ export const integrationFieldProperties: {
|
|||||||
export type IntegrationFieldDefinitionType = {
|
export type IntegrationFieldDefinitionType = {
|
||||||
type: 'private' | 'public';
|
type: 'private' | 'public';
|
||||||
icon: TablerIcon;
|
icon: TablerIcon;
|
||||||
iconUnset: TablerIcon;
|
|
||||||
label: string;
|
label: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -99,19 +90,16 @@ export const integrationFieldDefinitions: {
|
|||||||
apiKey: {
|
apiKey: {
|
||||||
type: 'private',
|
type: 'private',
|
||||||
icon: IconKey,
|
icon: IconKey,
|
||||||
iconUnset: IconKeyOff,
|
|
||||||
label: 'API Key',
|
label: 'API Key',
|
||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
type: 'public',
|
type: 'public',
|
||||||
icon: IconUser,
|
icon: IconUser,
|
||||||
iconUnset: IconUserOff,
|
|
||||||
label: 'Username',
|
label: 'Username',
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
type: 'private',
|
type: 'private',
|
||||||
icon: IconPassword,
|
icon: IconPassword,
|
||||||
iconUnset: IconLockOff,
|
|
||||||
label: 'Password',
|
label: 'Password',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user