Add categories!

This commit is contained in:
ajnart
2022-05-29 10:45:49 +02:00
parent c7c76ee22b
commit 1a2e752281
4 changed files with 111 additions and 31 deletions

View File

@@ -82,7 +82,6 @@ function MatchPort(name: string, form: any) {
]; ];
// Match name with portmap key // Match name with portmap key
const port = portmap.find((p) => p.name === name); const port = portmap.find((p) => p.name === name);
console.log('port', port);
if (port) { if (port) {
form.setFieldValue('url', `http://localhost:${port.value}`); form.setFieldValue('url', `http://localhost:${port.value}`);
} }
@@ -93,10 +92,19 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
const { config, setConfig } = useConfig(); const { config, setConfig } = useConfig();
const [isLoading, setLoading] = useState(false); const [isLoading, setLoading] = useState(false);
// Extract all the categories from the services in config
const categoryList = config.services.reduce((acc, cur) => {
if (cur.category && !acc.includes(cur.category)) {
acc.push(cur.category);
}
return acc;
}, [] as string[]);
const form = useForm({ const form = useForm({
initialValues: { initialValues: {
id: props.id ?? uuidv4(), id: props.id ?? uuidv4(),
type: props.type ?? 'Other', type: props.type ?? 'Other',
category: props.category ?? undefined,
name: props.name ?? '', name: props.name ?? '',
icon: props.icon ?? '/favicon.svg', icon: props.icon ?? '/favicon.svg',
url: props.url ?? '', url: props.url ?? '',
@@ -126,6 +134,15 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
}, },
}); });
// Try to set const hostname to new URL(form.values.url).hostname)
// If it fails, set it to the form.values.url
let hostname = form.values.url;
try {
hostname = new URL(form.values.url).origin;
} catch (e) {
// Do nothing
}
return ( return (
<> <>
<Center> <Center>
@@ -200,6 +217,21 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
data={ServiceTypeList} data={ServiceTypeList}
{...form.getInputProps('type')} {...form.getInputProps('type')}
/> />
<Select
label="Category"
data={categoryList}
placeholder="Select a category or create a new one"
nothingFound="Nothing found"
searchable
clearable
creatable
onClick={(e) => {
e.preventDefault();
}}
getCreateLabel={(query) => `+ Create "${query}"`}
onCreate={(query) => {}}
{...form.getInputProps('category')}
/>
<LoadingOverlay visible={isLoading} /> <LoadingOverlay visible={isLoading} />
{(form.values.type === 'Sonarr' || {(form.values.type === 'Sonarr' ||
form.values.type === 'Radarr' || form.values.type === 'Radarr' ||
@@ -229,7 +261,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
target="_blank" target="_blank"
weight="bold" weight="bold"
style={{ fontStyle: 'inherit', fontSize: 'inherit' }} style={{ fontStyle: 'inherit', fontSize: 'inherit' }}
href={`${form.values.url}/settings/general`} href={`${hostname}/settings/general`}
> >
here here
</Anchor> </Anchor>

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Grid } from '@mantine/core'; import { Grid, Group, Title } from '@mantine/core';
import { import {
closestCenter, closestCenter,
DndContext, DndContext,
@@ -45,35 +45,81 @@ const AppShelf = (props: any) => {
setActiveId(null); setActiveId(null);
} }
// Extract all the categories from the services in config
const categoryList = config.services.reduce((acc, cur) => {
if (cur.category && !acc.includes(cur.category)) {
acc.push(cur.category);
}
return acc;
}, [] as string[]);
return ( const item = (filter?: string) => {
<DndContext // If filter is not set, return all the services without a category or a null category
sensors={sensors} let filtered = config.services;
collisionDetection={closestCenter} if (!filter) {
onDragStart={handleDragStart} filtered = config.services.filter((e) => !e.category || e.category === null);
onDragEnd={handleDragEnd} }
> if (filter) {
<SortableContext items={config.services}> filtered = config.services.filter((e) => e.category === filter);
<Grid gutter="xl" align="center"> }
{config.services.map((service) => (
<Grid.Col key={service.id} span={6} xl={2} xs={4} sm={3} md={3}> return (
<SortableAppShelfItem service={service} key={service.id} id={service.id} /> <DndContext
</Grid.Col> sensors={sensors}
))} collisionDetection={closestCenter}
</Grid> onDragStart={handleDragStart}
</SortableContext> onDragEnd={handleDragEnd}
<DragOverlay
style={{
// Add a shadow to the drag overlay
boxShadow: '0 0 10px rgba(0, 0, 0, 0.5)',
}}
> >
{activeId ? ( <SortableContext items={config.services}>
<AppShelfItem service={config.services.find((e) => e.id === activeId)} id={activeId} /> <Grid gutter="xl" align="center">
{filtered.map((service) => (
<Grid.Col key={service.id} span={6} xl={2} xs={4} sm={3} md={3}>
<SortableAppShelfItem service={service} key={service.id} id={service.id} />
</Grid.Col>
))}
</Grid>
</SortableContext>
<DragOverlay
style={{
// Add a shadow to the drag overlay
boxShadow: '0 0 10px rgba(0, 0, 0, 0.5)',
}}
>
{activeId ? (
<AppShelfItem service={config.services.find((e) => e.id === activeId)} id={activeId} />
) : null}
</DragOverlay>
</DndContext>
);
};
if (categoryList.length > 0) {
const noCategory = config.services.filter(
(e) => e.category === undefined || e.category === null
);
return (
// Return one item for each category
<Group grow direction="column">
{categoryList.map((category) => (
<>
<Title order={3} key={category}>
{category}
</Title>
{item(category)}
</>
))}
{/* Return the item for all services without category */}
{noCategory && noCategory.length > 0 ? (
<>
<Title order={3}>Other</Title>
{item()}
</>
) : null} ) : null}
</DragOverlay> </Group>
</DndContext> );
); }
return item();
}; };
export default AppShelf; export default AppShelf;

View File

@@ -24,6 +24,7 @@ export default function AppShelfMenu(props: any) {
setOpened={setOpened} setOpened={setOpened}
name={service.name} name={service.name}
id={service.id} id={service.id}
category={service.category}
type={service.type} type={service.type}
url={service.url} url={service.url}
icon={service.icon} icon={service.icon}
@@ -47,7 +48,7 @@ export default function AppShelfMenu(props: any) {
<Menu.Label>Settings</Menu.Label> <Menu.Label>Settings</Menu.Label>
<Menu.Item <Menu.Item
color="primary" color="primary"
icon={<Edit size={14} />} icon={<Edit />}
// TODO: #2 Add the ability to edit the service. // TODO: #2 Add the ability to edit the service.
onClick={() => setOpened(true)} onClick={() => setOpened(true)}
> >
@@ -73,7 +74,7 @@ export default function AppShelfMenu(props: any) {
message: undefined, message: undefined,
}); });
}} }}
icon={<Trash size={14} />} icon={<Trash />}
> >
Delete Delete
</Menu.Item> </Menu.Item>

View File

@@ -49,6 +49,7 @@ export interface serviceItem {
type: string; type: string;
url: string; url: string;
icon: string; icon: string;
category?: string;
apiKey?: string; apiKey?: string;
password?: string; password?: string;
username?: string; username?: string;