App and sorting updates
This commit is contained in:
@@ -132,6 +132,8 @@ export function useMutationAPI() {
|
|||||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/links'] });
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/links'] });
|
||||||
endpoint === 'api/categoryOrder' &&
|
endpoint === 'api/categoryOrder' &&
|
||||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/categories'] });
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/categories'] });
|
||||||
|
endpoint === 'api/applicationOrder' &&
|
||||||
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/applicationOrder'] });
|
||||||
},
|
},
|
||||||
onSuccess: (data, { method, endpoint }) => {
|
onSuccess: (data, { method, endpoint }) => {
|
||||||
queryClient.refetchQueries({
|
queryClient.refetchQueries({
|
||||||
@@ -141,6 +143,8 @@ export function useMutationAPI() {
|
|||||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/links'] });
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/links'] });
|
||||||
endpoint === 'api/categoryOrder' &&
|
endpoint === 'api/categoryOrder' &&
|
||||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/categories'] });
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/categories'] });
|
||||||
|
endpoint === 'api/applicationOrder' &&
|
||||||
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/applicationOrder'] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ export function useCommonHooks() {
|
|||||||
key: 'appFilter',
|
key: 'appFilter',
|
||||||
defaultValue: ['All'],
|
defaultValue: ['All'],
|
||||||
});
|
});
|
||||||
|
const [appSort, setAppSort] = useLocalStorage<string>({
|
||||||
|
key: 'appSort',
|
||||||
|
defaultValue: 'A-Z',
|
||||||
|
});
|
||||||
|
const [bookmarkSort, setBookmarkSort] = useLocalStorage<string>({
|
||||||
|
key: 'bookmarkSort',
|
||||||
|
defaultValue: 'A-Z',
|
||||||
|
});
|
||||||
const [bookmarkFilter, setBookmarkFilter] = useLocalStorage<[any]>({
|
const [bookmarkFilter, setBookmarkFilter] = useLocalStorage<[any]>({
|
||||||
key: 'bookmarkFilter',
|
key: 'bookmarkFilter',
|
||||||
defaultValue: ['All'],
|
defaultValue: ['All'],
|
||||||
@@ -114,6 +122,10 @@ export function useCommonHooks() {
|
|||||||
setThemeName,
|
setThemeName,
|
||||||
appFilter,
|
appFilter,
|
||||||
setAppFilter,
|
setAppFilter,
|
||||||
|
appSort,
|
||||||
|
setAppSort,
|
||||||
|
bookmarkSort,
|
||||||
|
setBookmarkSort,
|
||||||
bookmarkFilter,
|
bookmarkFilter,
|
||||||
setBookmarkFilter,
|
setBookmarkFilter,
|
||||||
currentTheme,
|
currentTheme,
|
||||||
|
|||||||
@@ -9,17 +9,31 @@ function Applications({ contentType }: any) {
|
|||||||
const { data: apps, isFetching: isAppsFetching } = useQueryAPI({
|
const { data: apps, isFetching: isAppsFetching } = useQueryAPI({
|
||||||
endpoint: 'api/apps',
|
endpoint: 'api/apps',
|
||||||
});
|
});
|
||||||
const { isMobile } = useCommonHooks();
|
const { isMobile, appSort } = useCommonHooks();
|
||||||
const [applicationData, setApplicationData] = useState<Application[]>([]);
|
const [applicationData, setApplicationData] = useState<Application[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (apps && !isAppsFetching) {
|
if (apps && !isAppsFetching) {
|
||||||
setApplicationData(
|
setApplicationData(
|
||||||
apps.sort((a: Application, b: Application) => (a.name > b.name ? 1 : -1)),
|
apps
|
||||||
|
.filter((a: Application) => a.enabled === 1)
|
||||||
|
.sort((a: any, b: any) => {
|
||||||
|
switch (appSort) {
|
||||||
|
case 'A>Z':
|
||||||
|
return a.name > b.name ? 1 : -1;
|
||||||
|
case 'Z>A':
|
||||||
|
return a.name < b.name ? 1 : -1;
|
||||||
|
case 'Newest':
|
||||||
|
return a.id < b.id ? 1 : -1;
|
||||||
|
case 'Oldest':
|
||||||
|
return a.id > b.id ? 1 : -1;
|
||||||
|
case 'Custom':
|
||||||
|
return a.index > b.index ? 1 : -1;
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [apps, isAppsFetching]);
|
}, [apps, isAppsFetching, appSort]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{applicationData.length > 0 && contentType.length > 0 && (
|
{applicationData.length > 0 && contentType.length > 0 && (
|
||||||
@@ -39,11 +53,13 @@ function Applications({ contentType }: any) {
|
|||||||
gridTemplateRows: '64px',
|
gridTemplateRows: '64px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{applicationData?.map((application: any) => {
|
{applicationData
|
||||||
if (
|
?.filter(
|
||||||
contentType.includes(application.filterName) ||
|
(application: Application) =>
|
||||||
contentType.includes('All')
|
contentType.includes(application.filterName) ||
|
||||||
|
contentType.includes('All'),
|
||||||
)
|
)
|
||||||
|
.map((application: any) => {
|
||||||
return (
|
return (
|
||||||
<AppItem
|
<AppItem
|
||||||
key={application.name}
|
key={application.name}
|
||||||
@@ -53,7 +69,7 @@ function Applications({ contentType }: any) {
|
|||||||
category={application.category}
|
category={application.category}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ function Links({ contentType }: any) {
|
|||||||
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
||||||
endpoint: 'api/categories',
|
endpoint: 'api/categories',
|
||||||
});
|
});
|
||||||
const { isMobile } = useCommonHooks();
|
const { isMobile, bookmarkSort } = useCommonHooks();
|
||||||
const [bookmarkData, setBookmarkData] = useState<Bookmark[]>([]);
|
const [bookmarkData, setBookmarkData] = useState<Bookmark[]>([]);
|
||||||
const [groups, setGroups] = useState<BookmarkGroup[]>([]);
|
const [groups, setGroups] = useState<BookmarkGroup[]>([]);
|
||||||
|
|
||||||
@@ -28,12 +28,23 @@ function Links({ contentType }: any) {
|
|||||||
return category;
|
return category;
|
||||||
});
|
});
|
||||||
setGroups(
|
setGroups(
|
||||||
groupList.sort((a: BookmarkGroup, b: BookmarkGroup) =>
|
groupList.sort((a: any, b: any) => {
|
||||||
a.categoryIndex > b.categoryIndex ? 1 : -1,
|
switch (bookmarkSort) {
|
||||||
),
|
case 'A>Z':
|
||||||
|
return a.categoryName > b.categoryName ? 1 : -1;
|
||||||
|
case 'Z>A':
|
||||||
|
return a.categoryName < b.categoryName ? 1 : -1;
|
||||||
|
case 'Newest':
|
||||||
|
return a.id < b.id ? 1 : -1;
|
||||||
|
case 'Oldest':
|
||||||
|
return a.id > b.id ? 1 : -1;
|
||||||
|
case 'Custom':
|
||||||
|
return a.index > b.index ? 1 : -1;
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [groupData, groupsIsFetched]);
|
}, [groupData, groupsIsFetched, bookmarkSort]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -55,11 +66,14 @@ function Links({ contentType }: any) {
|
|||||||
gridTemplateRows: 'auto',
|
gridTemplateRows: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{groups.map((category: BookmarkGroup) => {
|
{groups
|
||||||
if (
|
?.filter(
|
||||||
contentType.includes(category.filterName) ||
|
(category: BookmarkGroup) =>
|
||||||
contentType.includes('All')
|
contentType.includes(category.filterName) ||
|
||||||
) {
|
contentType.includes('All'),
|
||||||
|
)
|
||||||
|
|
||||||
|
.map((category: BookmarkGroup) => {
|
||||||
return (
|
return (
|
||||||
<LinkGroups
|
<LinkGroups
|
||||||
key={category.categoryName}
|
key={category.categoryName}
|
||||||
@@ -70,8 +84,7 @@ function Links({ contentType }: any) {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
})}
|
||||||
})}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ function SettingsButton() {
|
|||||||
theme,
|
theme,
|
||||||
appFilter,
|
appFilter,
|
||||||
setAppFilter,
|
setAppFilter,
|
||||||
|
appSort,
|
||||||
|
setAppSort,
|
||||||
|
bookmarkSort,
|
||||||
|
setBookmarkSort,
|
||||||
bookmarkFilter,
|
bookmarkFilter,
|
||||||
setBookmarkFilter,
|
setBookmarkFilter,
|
||||||
isMobile,
|
isMobile,
|
||||||
@@ -330,6 +334,57 @@ function SettingsButton() {
|
|||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Menu.Divider
|
||||||
|
w={'85%'}
|
||||||
|
m={'.75rem'}
|
||||||
|
style={{ borderColor: theme.accentColor }}
|
||||||
|
/>
|
||||||
|
<Stack
|
||||||
|
gap={0}
|
||||||
|
align='center'
|
||||||
|
w={'85%'}
|
||||||
|
h={'100px'}
|
||||||
|
className={stackClasses.menuStack}
|
||||||
|
classNames={stackClasses}
|
||||||
|
>
|
||||||
|
<Menu.Label>Sorting</Menu.Label>
|
||||||
|
<Group justify='space-between' px={'.5rem'}>
|
||||||
|
<Select
|
||||||
|
label={'Apps'}
|
||||||
|
placeholder='Sort Applications...'
|
||||||
|
value={appSort}
|
||||||
|
data={['Custom', 'A>Z', 'Z>A', 'Newest', 'Oldest']}
|
||||||
|
w={'45%'}
|
||||||
|
classNames={{
|
||||||
|
input: selectClasses.menuSelectInput,
|
||||||
|
label: selectClasses.menuSelectLabel,
|
||||||
|
options: selectClasses.menuSelectOptions,
|
||||||
|
option: selectClasses.menuSelectOption,
|
||||||
|
}}
|
||||||
|
comboboxProps={{ withinPortal: false }}
|
||||||
|
onChange={(_value, option: any) => {
|
||||||
|
setAppSort(option.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
label={'Bookmarks'}
|
||||||
|
placeholder='Sort Bookmarks...'
|
||||||
|
value={bookmarkSort}
|
||||||
|
data={['Custom', 'A>Z', 'Z>A', 'Newest', 'Oldest']}
|
||||||
|
w={'45%'}
|
||||||
|
classNames={{
|
||||||
|
input: selectClasses.menuSelectInput,
|
||||||
|
label: selectClasses.menuSelectLabel,
|
||||||
|
options: selectClasses.menuSelectOptions,
|
||||||
|
option: selectClasses.menuSelectOption,
|
||||||
|
}}
|
||||||
|
comboboxProps={{ withinPortal: false }}
|
||||||
|
onChange={(_value, option: any) => {
|
||||||
|
setBookmarkSort(option.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Menu.Dropdown>
|
</Menu.Dropdown>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@@ -1,74 +1,91 @@
|
|||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { Box, Stack, Text } from '@mantine/core';
|
import { ActionIcon, Box, Stack, Text, Tooltip } from '@mantine/core';
|
||||||
import { useCommonHooks } from 'hooks';
|
import { useCommonHooks } from 'hooks';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { boxClasses, textClasses } from 'styles';
|
import { FiAlertCircle } from 'react-icons/fi';
|
||||||
|
import { actionIconClasses, boxClasses, textClasses } from 'styles';
|
||||||
|
|
||||||
function AppSettingsItem({ url, icon, name }: any) {
|
function AppSettingsItem({ url, icon, name, enabled }: any) {
|
||||||
const [textDecoration, setTextDecoration] = useState<string>('none');
|
const [textDecoration, setTextDecoration] = useState<string>('none');
|
||||||
const [hover, setHover] = useState<boolean>(false);
|
const [hover, setHover] = useState<boolean>(false);
|
||||||
const { isMobile } = useCommonHooks();
|
const { isMobile, theme } = useCommonHooks();
|
||||||
return (
|
return (
|
||||||
<Box
|
<>
|
||||||
component='a'
|
{enabled === 0 && (
|
||||||
onMouseOver={() => {
|
<Tooltip label={<Text c={theme.primaryColor}>This app is disabled</Text>}>
|
||||||
setTextDecoration('underline');
|
<ActionIcon
|
||||||
setHover(true);
|
className={actionIconClasses.settingsTooltip}
|
||||||
}}
|
classNames={actionIconClasses}
|
||||||
onMouseOut={() => {
|
pos={'absolute'}
|
||||||
setTextDecoration('none');
|
size={'xl'}
|
||||||
setHover(false);
|
variant='subtle'
|
||||||
}}
|
style={{ zIndex: 1 }}
|
||||||
className={boxClasses.appItemBox}
|
>
|
||||||
>
|
<FiAlertCircle color='red' />
|
||||||
{hover && (
|
</ActionIcon>
|
||||||
<svg
|
</Tooltip>
|
||||||
style={{ position: 'absolute', borderRadius: '6px' }}
|
|
||||||
height='100%'
|
|
||||||
width='100%'
|
|
||||||
xmlns='http://www.w3.org/2000/svg'
|
|
||||||
>
|
|
||||||
<rect rx='16' ry='16' height='100%' width='100%' strokeLinejoin='round' />
|
|
||||||
</svg>
|
|
||||||
)}
|
)}
|
||||||
<Stack
|
<Box
|
||||||
align={isMobile ? 'center' : undefined}
|
component='a'
|
||||||
justify={isMobile ? 'center' : undefined}
|
onMouseOver={() => {
|
||||||
style={{
|
setTextDecoration('underline');
|
||||||
height: '128px',
|
setHover(true);
|
||||||
width: isMobile ? '100%' : undefined,
|
|
||||||
marginRight: isMobile ? '0' : '1em',
|
|
||||||
marginLeft: isMobile ? '0' : '2em',
|
|
||||||
paddingTop: isMobile ? '0' : '32px',
|
|
||||||
}}
|
}}
|
||||||
>
|
onMouseOut={() => {
|
||||||
<Icon icon={`mdi:${icon}`} height={64} width={64} />
|
setTextDecoration('none');
|
||||||
</Stack>
|
setHover(false);
|
||||||
<Stack
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flex: 1,
|
|
||||||
overflow: 'hidden',
|
|
||||||
gap: 0,
|
|
||||||
}}
|
}}
|
||||||
|
className={boxClasses.appItemBox}
|
||||||
>
|
>
|
||||||
<Text fz={22} className={textClasses.appName} classNames={textClasses}>
|
{hover && (
|
||||||
{name}
|
<svg
|
||||||
</Text>
|
style={{ position: 'absolute', borderRadius: '6px' }}
|
||||||
<Text
|
height='100%'
|
||||||
fz={16}
|
width='100%'
|
||||||
className={textClasses.appUrl}
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
classNames={textClasses}
|
>
|
||||||
|
<rect rx='16' ry='16' height='100%' width='100%' strokeLinejoin='round' />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
<Stack
|
||||||
|
align={isMobile ? 'center' : undefined}
|
||||||
|
justify={isMobile ? 'center' : undefined}
|
||||||
style={{
|
style={{
|
||||||
textDecoration: textDecoration,
|
height: '128px',
|
||||||
|
width: isMobile ? '100%' : undefined,
|
||||||
|
marginRight: isMobile ? '0' : '1em',
|
||||||
|
marginLeft: isMobile ? '0' : '2em',
|
||||||
|
paddingTop: isMobile ? '0' : '32px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{url}
|
<Icon icon={`mdi:${icon}`} height={64} width={64} />
|
||||||
</Text>
|
</Stack>
|
||||||
</Stack>
|
<Stack
|
||||||
</Box>
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flex: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
gap: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text fz={22} className={textClasses.appName} classNames={textClasses}>
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
fz={16}
|
||||||
|
className={textClasses.appUrl}
|
||||||
|
classNames={textClasses}
|
||||||
|
style={{
|
||||||
|
textDecoration: textDecoration,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{url}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Box,
|
Box,
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
Modal,
|
Modal,
|
||||||
Popover,
|
Popover,
|
||||||
Stack,
|
Stack,
|
||||||
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
@@ -36,19 +38,50 @@ const ApplicationSettings = () => {
|
|||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const [applicationData, setApplicationData] = useState<Application[]>([]);
|
const [applicationData, setApplicationData] = useState<Application[]>([]);
|
||||||
|
const [applicationList, setApplicationList] = useState<Application[]>([]);
|
||||||
const { theme, isMobile, modalStyle } = useCommonHooks();
|
const { theme, isMobile, modalStyle } = useCommonHooks();
|
||||||
const [newApplication, setNewApplication] = useState<Application>({
|
const [newApplication, setNewApplication] = useState<Application>({
|
||||||
id: undefined,
|
id: null,
|
||||||
name: '',
|
name: '',
|
||||||
url: '',
|
url: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
filterName: '',
|
filterName: '',
|
||||||
index: 0,
|
index: 0,
|
||||||
|
enabled: 1,
|
||||||
});
|
});
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
|
const [dragging, setDragging] = useState(false);
|
||||||
|
|
||||||
|
const reorder = (list: any, startIndex: any, endIndex: any) => {
|
||||||
|
const result = Array.from(list);
|
||||||
|
const [removed]: any = result.splice(startIndex, 1);
|
||||||
|
result.splice(endIndex, 0, removed);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
function onDragEnd(result: any) {
|
||||||
|
if (!result.destination) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.destination.index === result.source.index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = reorder(applicationData, result.source.index, result.destination.index);
|
||||||
|
setApplicationList(
|
||||||
|
items.map((item: any, index: number) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -58,11 +91,21 @@ const ApplicationSettings = () => {
|
|||||||
.filter((a: Application) =>
|
.filter((a: Application) =>
|
||||||
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()),
|
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()),
|
||||||
)
|
)
|
||||||
.sort((a: Application, b: Application) => (a.name > b.name ? 1 : -1)),
|
.sort((a: Application, b: Application) => (a.index > b.index ? 1 : -1)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [apps, isAppsFetching, debouncedSearch]);
|
}, [apps, isAppsFetching, debouncedSearch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (applicationList.length > 0 && applicationList !== applicationData) {
|
||||||
|
mutateAsync({
|
||||||
|
method: 'PATCH',
|
||||||
|
endpoint: 'api/applicationOrder',
|
||||||
|
body: applicationList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [applicationList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack w={'100%'}>
|
<Stack w={'100%'}>
|
||||||
<Modal.Root
|
<Modal.Root
|
||||||
@@ -266,33 +309,132 @@ const ApplicationSettings = () => {
|
|||||||
setSearchValue(event.target.value);
|
setSearchValue(event.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Switch
|
||||||
|
label={<Text c={theme.primaryColor}>Reorder</Text>}
|
||||||
|
labelPosition='left'
|
||||||
|
pos={'absolute'}
|
||||||
|
right={isMobile ? '2rem' : '1rem'}
|
||||||
|
top={isMobile ? '10rem' : undefined}
|
||||||
|
onChange={(e) => {
|
||||||
|
setDragging(e.target.checked);
|
||||||
|
}}
|
||||||
|
color={theme.primaryColor}
|
||||||
|
styles={{
|
||||||
|
label: {
|
||||||
|
color: theme.primaryColor,
|
||||||
|
top: isMobile ? -22 : 0,
|
||||||
|
left: isMobile ? -8 : 0,
|
||||||
|
position: isMobile ? 'absolute' : undefined,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
<Stack w={'100%'} align='center'>
|
{dragging ? (
|
||||||
<Grid columns={18} w={'75%'}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
{applicationData?.map((application: Application) => (
|
<Stack w={'100%'} align='center'>
|
||||||
<GridCol span={6} key={application.id}>
|
<Droppable droppableId={'groupList'}>
|
||||||
<Popover withArrow withOverlay withinPortal={false}>
|
{(provided) => (
|
||||||
<Popover.Target>
|
<Stack
|
||||||
<Box
|
w={'75%'}
|
||||||
onClick={(e) => {
|
align='center'
|
||||||
e.stopPropagation();
|
justify='center'
|
||||||
}}
|
ref={provided.innerRef}
|
||||||
>
|
{...provided.droppableProps}
|
||||||
<AppSettingsItem
|
>
|
||||||
url={application.url}
|
{applicationData?.map((app: Application) => (
|
||||||
icon={application.icon}
|
<Draggable
|
||||||
name={application.name}
|
key={app.id}
|
||||||
/>
|
draggableId={`${app.id}`}
|
||||||
</Box>
|
index={Number(app.index)}
|
||||||
</Popover.Target>
|
>
|
||||||
<Popover.Dropdown>
|
{(provided) => (
|
||||||
<DebouncedApplication application={application} />
|
<Box
|
||||||
</Popover.Dropdown>
|
ref={provided.innerRef}
|
||||||
</Popover>
|
{...provided.draggableProps}
|
||||||
</GridCol>
|
{...provided.dragHandleProps}
|
||||||
))}
|
>
|
||||||
</Grid>
|
<Card
|
||||||
</Stack>
|
w={'400px'}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.searchColor,
|
||||||
|
border: '1px solid var(--color-border)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Group gap={0} align='center' justify='space-between'>
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text c={theme.accentColor} fz={14}>
|
||||||
|
Name
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
p={4}
|
||||||
|
style={{
|
||||||
|
borderRadius: '4px',
|
||||||
|
paddingLeft: '8px',
|
||||||
|
color: theme.primaryColor,
|
||||||
|
backgroundColor: theme.searchColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{app.name}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text c={theme.accentColor} fz={14}>
|
||||||
|
Filter Name
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
p={4}
|
||||||
|
style={{
|
||||||
|
borderRadius: '4px',
|
||||||
|
paddingLeft: '8px',
|
||||||
|
color: theme.primaryColor,
|
||||||
|
backgroundColor: theme.searchColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{app.filterName}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
{provided.placeholder}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</Stack>
|
||||||
|
</DragDropContext>
|
||||||
|
) : (
|
||||||
|
<Stack w={'100%'} align='center'>
|
||||||
|
<Grid columns={18} w={'75%'}>
|
||||||
|
{applicationData?.map((application: Application) => (
|
||||||
|
<GridCol span={6} key={application.id}>
|
||||||
|
<Popover withArrow withOverlay withinPortal={false}>
|
||||||
|
<Popover.Target>
|
||||||
|
<Box
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppSettingsItem
|
||||||
|
url={application.url}
|
||||||
|
icon={application.icon}
|
||||||
|
name={application.name}
|
||||||
|
enabled={application.enabled}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Popover.Target>
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<DebouncedApplication application={application} />
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
</GridCol>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
Modal,
|
Modal,
|
||||||
Stack,
|
Stack,
|
||||||
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -36,7 +37,6 @@ function DebouncedApplication({ application }: { application: Application }) {
|
|||||||
setApp(update);
|
setApp(update);
|
||||||
}
|
}
|
||||||
}, [update]);
|
}, [update]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
|
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
|
||||||
<Modal.Root
|
<Modal.Root
|
||||||
@@ -114,6 +114,27 @@ function DebouncedApplication({ application }: { application: Application }) {
|
|||||||
<IconTrash />
|
<IconTrash />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
color={theme.primaryColor}
|
||||||
|
bd={`solid 1px ${theme.borderColor}`}
|
||||||
|
label={'Enabled'}
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
label={<Text c={theme.primaryColor}>Enabled</Text>}
|
||||||
|
labelPosition='left'
|
||||||
|
pos={'absolute'}
|
||||||
|
left={16}
|
||||||
|
defaultChecked={app.enabled === 1}
|
||||||
|
defaultValue={app.enabled}
|
||||||
|
onChange={(event) => {
|
||||||
|
setUpdate({
|
||||||
|
...app,
|
||||||
|
enabled: event.target.checked ? 1 : 0,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
color={theme.primaryColor}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<Group w={'100%'} justify='center'>
|
<Group w={'100%'} justify='center'>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={'Name'}
|
label={'Name'}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
.inputField {
|
.inputField {
|
||||||
color: var(--color-text-acc) !important;
|
color: var(--color-text-acc) !important;
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: var(--color-text-acc) !important;
|
color: var(--color-text-pri) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
export interface Application {
|
export interface Application {
|
||||||
id: number | undefined;
|
id: number | null;
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
filterName: string;
|
filterName: string;
|
||||||
index: number;
|
index: number;
|
||||||
|
enabled: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppCategory {
|
export interface AppCategory {
|
||||||
|
|||||||
Reference in New Issue
Block a user