Files
Homarr/src/components/AppShelf/AddAppShelfItem.tsx

200 lines
5.3 KiB
TypeScript
Raw Normal View History

2022-04-25 23:33:32 +02:00
import {
Modal,
Center,
Group,
TextInput,
Image,
Button,
Select,
LoadingOverlay,
ActionIcon,
Tooltip,
Title,
2022-04-25 23:33:32 +02:00
} from '@mantine/core';
import { useForm } from '@mantine/form';
2022-04-25 23:33:32 +02:00
import { useState } from 'react';
import { Apps } from 'tabler-icons-react';
import { v4 as uuidv4 } from 'uuid';
2022-05-03 19:52:09 +02:00
import { useConfig } from '../../tools/state';
import { ServiceTypeList } from '../../tools/types';
2022-04-25 23:33:32 +02:00
export function AddItemShelfButton(props: any) {
const [opened, setOpened] = useState(false);
return (
<>
<Modal
size="xl"
radius="md"
title={<Title order={3}>Add service</Title>}
opened={props.opened || opened}
onClose={() => setOpened(false)}
>
<AddAppShelfItemForm setOpened={setOpened} />
</Modal>
<ActionIcon
variant="default"
radius="md"
size="xl"
color="blue"
style={props.style}
onClick={() => setOpened(true)}
>
<Tooltip label="Add a service">
<Apps />
</Tooltip>
</ActionIcon>
</>
);
}
2022-05-12 14:14:48 +02:00
function MatchIcon(name: string, form: any) {
2022-05-12 14:24:15 +02:00
fetch(
`https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/${name
.replace(/\s+/g, '-')
.toLowerCase()}.png`
).then((res) => {
if (res.ok) {
form.setFieldValue('icon', res.url);
}
});
return false;
}
2022-05-12 14:24:15 +02:00
export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & any) {
const { setOpened } = props;
const { config, setConfig } = useConfig();
const [isLoading, setLoading] = useState(false);
const form = useForm({
initialValues: {
2022-05-23 10:23:10 +02:00
id: props.id ?? uuidv4(),
type: props.type ?? 'Other',
name: props.name ?? '',
icon: props.icon ?? '/favicon.svg',
url: props.url ?? '',
apiKey: props.apiKey ?? (undefined as unknown as string),
},
validate: {
apiKey: () => null,
// Validate icon with a regex
icon: (value: string) => {
// Regex to match everything that ends with and icon extension
if (!value.match(/\.(png|jpg|jpeg|gif|svg)$/)) {
return 'Please enter a valid icon URL';
}
return null;
},
// Validate url with a regex http/https
url: (value: string) => {
try {
const _isValid = new URL(value);
} catch (e) {
return 'Please enter a valid URL';
}
return null;
},
},
});
return (
<>
<Center>
2022-05-17 21:36:07 +02:00
<Image
height={120}
width={120}
fit="contain"
src={form.values.icon}
alt="Placeholder"
withPlaceholder
/>
</Center>
<form
onSubmit={form.onSubmit(() => {
// If service already exists, update it.
2022-05-19 22:29:35 +02:00
if (config.services && config.services.find((s) => s.id === form.values.id)) {
setConfig({
...config,
2022-05-19 22:29:35 +02:00
// replace the found item by matching ID
services: config.services.map((s) => {
2022-05-19 22:29:35 +02:00
if (s.id === form.values.id) {
return {
...form.values,
};
}
return s;
}),
});
} else {
setConfig({
...config,
services: [...config.services, form.values],
});
}
setOpened(false);
form.reset();
})}
>
<Group direction="column" grow>
<TextInput
required
label="Service name"
placeholder="Plex"
value={form.values.name}
onChange={(event) => {
form.setFieldValue('name', event.currentTarget.value);
const match = MatchIcon(event.currentTarget.value, form);
if (match) {
form.setFieldValue('icon', match);
}
}}
error={form.errors.name && 'Invalid icon url'}
/>
<TextInput
required
label="Icon url"
placeholder="https://i.gifer.com/ANPC.gif"
{...form.getInputProps('icon')}
/>
<TextInput
required
label="Service url"
placeholder="http://localhost:7575"
{...form.getInputProps('url')}
/>
<Select
label="Select the type of service (used for API calls)"
defaultValue="Other"
placeholder="Pick one"
required
searchable
data={ServiceTypeList}
{...form.getInputProps('type')}
/>
<LoadingOverlay visible={isLoading} />
{(form.values.type === 'Sonarr' ||
form.values.type === 'Radarr' ||
form.values.type === 'Lidarr' ||
form.values.type === 'Readarr') && (
<TextInput
required
label="API key"
placeholder="Your API key"
value={form.values.apiKey}
onChange={(event) => {
form.setFieldValue('apiKey', event.currentTarget.value);
}}
error={form.errors.apiKey && 'Invalid API key'}
/>
)}
</Group>
<Group grow position="center" mt="xl">
<Button type="submit">{props.message ?? 'Add service'}</Button>
</Group>
</form>
</>
);
}