Add propagation for service name to service icon

This commit is contained in:
Manuel Ruwe
2022-12-11 19:32:51 +01:00
parent ed64d138c5
commit 05e47b5664
7 changed files with 97 additions and 74 deletions

View File

@@ -1,7 +1,6 @@
import { Alert, Button, createStyles, Group, Stack, Tabs, Text, ThemeIcon } from '@mantine/core'; import { Alert, Button, createStyles, Group, Stack, Tabs, Text, ThemeIcon } from '@mantine/core';
import { useForm } from '@mantine/form'; import { useForm } from '@mantine/form';
import { ContextModalProps } from '@mantine/modals'; import { ContextModalProps } from '@mantine/modals';
import { hideNotification, showNotification } from '@mantine/notifications';
import { import {
IconAccessPoint, IconAccessPoint,
IconAdjustments, IconAdjustments,
@@ -11,7 +10,6 @@ import {
IconPlug, IconPlug,
} from '@tabler/icons'; } from '@tabler/icons';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import Image from 'next/image';
import { useState } from 'react'; import { useState } from 'react';
import { useConfigContext } from '../../../../config/provider'; import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store'; import { useConfigStore } from '../../../../config/store';
@@ -31,11 +29,13 @@ export const EditServiceModal = ({
context, context,
id, id,
innerProps, innerProps,
}: ContextModalProps<{ service: ServiceType }>) => { }: ContextModalProps<{ service: ServiceType; allowServiceNamePropagation: boolean }>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { classes } = useStyles();
const { name: configName, config } = useConfigContext(); const { name: configName, config } = useConfigContext();
const updateConfig = useConfigStore((store) => store.updateConfig); const updateConfig = useConfigStore((store) => store.updateConfig);
const [allowServiceNamePropagation, setAllowServiceNamePropagation] = useState<boolean>(
innerProps.allowServiceNamePropagation
);
const form = useForm<ServiceType>({ const form = useForm<ServiceType>({
initialValues: innerProps.service, initialValues: innerProps.service,
@@ -94,29 +94,7 @@ export const EditServiceModal = ({
const [activeTab, setActiveTab] = useState<EditServiceModalTab>('general'); const [activeTab, setActiveTab] = useState<EditServiceModalTab>('general');
const tryCloseModal = () => { const closeModal = () => {
if (form.isDirty()) {
showNotification({
id: 'unsaved-edit-service-modal-changes',
title: 'You have unsaved changes',
message: (
<Stack>
<Text color="dimmed">If you close, your changes will be discarded and not saved.</Text>
<Button
onClick={() => {
context.closeModal(id);
hideNotification('unsaved-edit-service-modal-changes');
}}
variant="light"
>
Close anyway
</Button>
</Stack>
),
});
return;
}
context.closeModal(id); context.closeModal(id);
}; };
@@ -196,12 +174,16 @@ export const EditServiceModal = ({
<GeneralTab form={form} openTab={(targetTab) => setActiveTab(targetTab)} /> <GeneralTab form={form} openTab={(targetTab) => setActiveTab(targetTab)} />
<BehaviourTab form={form} /> <BehaviourTab form={form} />
<NetworkTab form={form} /> <NetworkTab form={form} />
<AppearanceTab form={form} /> <AppearanceTab
form={form}
disallowServiceNameProgagation={() => setAllowServiceNamePropagation(false)}
allowServiceNamePropagation={allowServiceNamePropagation}
/>
<IntegrationTab form={form} /> <IntegrationTab form={form} />
</Tabs> </Tabs>
<Group position="right" mt={100}> <Group position="right" mt={100}>
<Button onClick={tryCloseModal} px={50} variant="light" color="gray"> <Button onClick={closeModal} px={50} variant="light" color="gray">
Cancel Cancel
</Button> </Button>
<Button disabled={!form.isValid()} px={50} type="submit"> <Button disabled={!form.isValid()} px={50} type="submit">

View File

@@ -7,9 +7,15 @@ import { IconSelector } from './IconSelector/IconSelector';
interface AppearanceTabProps { interface AppearanceTabProps {
form: UseFormReturnType<ServiceType, (values: ServiceType) => ServiceType>; form: UseFormReturnType<ServiceType, (values: ServiceType) => ServiceType>;
disallowServiceNameProgagation: () => void;
allowServiceNamePropagation: boolean;
} }
export const AppearanceTab = ({ form }: AppearanceTabProps) => { export const AppearanceTab = ({
form,
disallowServiceNameProgagation,
allowServiceNamePropagation,
}: AppearanceTabProps) => {
const { t } = useTranslation(''); const { t } = useTranslation('');
const { classes } = useStyles(); const { classes } = useStyles();
@@ -28,13 +34,16 @@ export const AppearanceTab = ({ form }: AppearanceTabProps) => {
{...form.getInputProps('appearance.iconUrl')} {...form.getInputProps('appearance.iconUrl')}
/> />
<IconSelector <IconSelector
onChange={(item) => onChange={(item) => {
form.setValues({ form.setValues({
appearance: { appearance: {
iconUrl: item.url, iconUrl: item.url,
}, },
}) });
} disallowServiceNameProgagation();
}}
allowServiceNamePropagation={allowServiceNamePropagation}
form={form}
/> />
</Flex> </Flex>
</Tabs.Panel> </Tabs.Panel>

View File

@@ -13,22 +13,32 @@ import {
TextInput, TextInput,
Title, Title,
} from '@mantine/core'; } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { useDebouncedValue } from '@mantine/hooks';
import { IconSearch, IconX } from '@tabler/icons'; import { IconSearch, IconX } from '@tabler/icons';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { ICON_PICKER_SLICE_LIMIT } from '../../../../../../../../data/constants'; import { ICON_PICKER_SLICE_LIMIT } from '../../../../../../../../data/constants';
import { useRepositoryIconsQuery } from '../../../../../../../tools/hooks/useRepositoryIconsQuery'; import { useRepositoryIconsQuery } from '../../../../../../../tools/hooks/useRepositoryIconsQuery';
import { IconSelectorItem } from '../../../../../../../types/iconSelector/iconSelectorItem'; import { IconSelectorItem } from '../../../../../../../types/iconSelector/iconSelectorItem';
import { WalkxcodeRepositoryIcon } from '../../../../../../../types/iconSelector/repositories/walkxcodeIconRepository'; import { WalkxcodeRepositoryIcon } from '../../../../../../../types/iconSelector/repositories/walkxcodeIconRepository';
import { ServiceType } from '../../../../../../../types/service';
interface IconSelectorProps { interface IconSelectorProps {
form: UseFormReturnType<ServiceType, (values: ServiceType) => ServiceType>;
onChange: (icon: IconSelectorItem) => void; onChange: (icon: IconSelectorItem) => void;
allowServiceNamePropagation: boolean;
} }
export const IconSelector = ({ onChange }: IconSelectorProps) => { export const IconSelector = ({
onChange,
allowServiceNamePropagation,
form,
}: IconSelectorProps) => {
const { data, isLoading } = useRepositoryIconsQuery<WalkxcodeRepositoryIcon>({ const { data, isLoading } = useRepositoryIconsQuery<WalkxcodeRepositoryIcon>({
url: 'https://api.github.com/repos/walkxcode/Dashboard-Icons/contents/png', url: 'https://api.github.com/repos/walkxcode/Dashboard-Icons/contents/png',
converter: (item) => ({ converter: (item) => ({
url: `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${item.name}`, url: `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${item.name}`,
fileName: item.name,
}), }),
}); });
@@ -39,8 +49,7 @@ export const IconSelector = ({ onChange }: IconSelectorProps) => {
return <Loader />; return <Loader />;
} }
const replaceCharacters = (value: string) => const replaceCharacters = (value: string) => value.toLowerCase().replaceAll('', '-');
value.toLowerCase().replaceAll(' ', '').replaceAll('-', '');
const filteredItems = searchTerm const filteredItems = searchTerm
? data.filter((x) => replaceCharacters(x.url).includes(replaceCharacters(searchTerm))) ? data.filter((x) => replaceCharacters(x.url).includes(replaceCharacters(searchTerm)))
@@ -49,6 +58,24 @@ export const IconSelector = ({ onChange }: IconSelectorProps) => {
const isTruncated = const isTruncated =
slicedFilteredItems.length > 0 && slicedFilteredItems.length !== filteredItems.length; slicedFilteredItems.length > 0 && slicedFilteredItems.length !== filteredItems.length;
const [debouncedValue] = useDebouncedValue(form.values.name, 500);
useEffect(() => {
if (allowServiceNamePropagation !== true) {
return;
}
const matchingDebouncedIcon = data.find(
(x) => replaceCharacters(x.fileName.split('.')[0]) === replaceCharacters(debouncedValue)
);
if (!matchingDebouncedIcon) {
return;
}
form.setFieldValue('appearance.iconUrl', matchingDebouncedIcon.url);
}, [debouncedValue]);
return ( return (
<Popover width={310}> <Popover width={310}>
<Popover.Target> <Popover.Target>

View File

@@ -1,4 +1,4 @@
import { Group, Tabs, Text, TextInput } from '@mantine/core'; import { Tabs, Text, TextInput } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form'; import { UseFormReturnType } from '@mantine/form';
import { IconCursorText, IconLink } from '@tabler/icons'; import { IconCursorText, IconLink } from '@tabler/icons';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';

View File

@@ -27,7 +27,8 @@ export const AvailableElementTypes = ({
name="Service" name="Service"
icon={<IconBox size={40} strokeWidth={1.3} />} icon={<IconBox size={40} strokeWidth={1.3} />}
onClick={() => { onClick={() => {
openContextModalGeneric<{ service: ServiceType }>({ openContextModalGeneric<{ service: ServiceType; allowServiceNamePropagation: boolean }>(
{
modal: 'editService', modal: 'editService',
innerProps: { innerProps: {
service: { service: {
@@ -62,9 +63,11 @@ export const AvailableElementTypes = ({
}, },
}, },
}, },
allowServiceNamePropagation: true,
}, },
size: 'xl', size: 'xl',
}); }
);
}} }}
/> />
<ElementItem <ElementItem

View File

@@ -8,11 +8,12 @@ interface TileMenuProps {
export const ServiceMenu = ({ service }: TileMenuProps) => { export const ServiceMenu = ({ service }: TileMenuProps) => {
const handleClickEdit = () => { const handleClickEdit = () => {
openContextModalGeneric<{ service: ServiceType }>({ openContextModalGeneric<{ service: ServiceType; allowServiceNamePropagation: boolean }>({
modal: 'editService', modal: 'editService',
size: 'xl', size: 'xl',
innerProps: { innerProps: {
service, service,
allowServiceNamePropagation: false,
}, },
}); });
}; };

View File

@@ -1,3 +1,4 @@
export interface IconSelectorItem { export interface IconSelectorItem {
url: string; url: string;
fileName: string;
} }