Drag and drop bookmark settings
This commit is contained in:
84
package-lock.json
generated
84
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "triggerfrontend",
|
"name": "triggerfrontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hello-pangea/dnd": "^18.0.1",
|
||||||
"@iconify/react": "^5.2.0",
|
"@iconify/react": "^5.2.0",
|
||||||
"@mantine/core": "^7.17.2",
|
"@mantine/core": "^7.17.2",
|
||||||
"@mantine/hooks": "^7.17.2",
|
"@mantine/hooks": "^7.17.2",
|
||||||
@@ -294,9 +295,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.24.5",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||||
"integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==",
|
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
},
|
},
|
||||||
@@ -981,6 +983,23 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
||||||
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="
|
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@hello-pangea/dnd": {
|
||||||
|
"version": "18.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-18.0.1.tgz",
|
||||||
|
"integrity": "sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.26.7",
|
||||||
|
"css-box-model": "^1.2.1",
|
||||||
|
"raf-schd": "^4.0.3",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
|
"redux": "^5.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanfs/core": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||||
@@ -1833,6 +1852,12 @@
|
|||||||
"@types/react": "^19.0.0"
|
"@types/react": "^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/use-sync-external-store": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.27.0",
|
"version": "8.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz",
|
||||||
@@ -2607,6 +2632,15 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-box-model": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tiny-invariant": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
@@ -4417,6 +4451,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/raf-schd": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.0.0",
|
"version": "19.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
||||||
@@ -4483,6 +4523,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-redux": {
|
||||||
|
"version": "9.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
|
"use-sync-external-store": "^1.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^18.2.25 || ^19",
|
||||||
|
"react": "^18.0 || ^19",
|
||||||
|
"redux": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"redux": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.14.2",
|
"version": "0.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
|
||||||
@@ -4688,6 +4751,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/redux": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.14.1",
|
"version": "0.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
@@ -5423,6 +5492,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/victory-vendor": {
|
"node_modules/victory-vendor": {
|
||||||
"version": "36.9.2",
|
"version": "36.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hello-pangea/dnd": "^18.0.1",
|
||||||
"@iconify/react": "^5.2.0",
|
"@iconify/react": "^5.2.0",
|
||||||
"@mantine/core": "^7.17.2",
|
"@mantine/core": "^7.17.2",
|
||||||
"@mantine/hooks": "^7.17.2",
|
"@mantine/hooks": "^7.17.2",
|
||||||
|
|||||||
@@ -127,14 +127,20 @@ export function useMutationAPI() {
|
|||||||
onMutate: ({ method, endpoint }) => {
|
onMutate: ({ method, endpoint }) => {
|
||||||
queryClient.refetchQueries({
|
queryClient.refetchQueries({
|
||||||
queryKey: ['GET', endpoint],
|
queryKey: ['GET', endpoint],
|
||||||
force: true,
|
|
||||||
});
|
});
|
||||||
|
endpoint === 'api/linkOrder' &&
|
||||||
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/links'] });
|
||||||
|
endpoint === 'api/categoryOrder' &&
|
||||||
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/categories'] });
|
||||||
},
|
},
|
||||||
onSuccess: (data, { method, endpoint }) => {
|
onSuccess: (data, { method, endpoint }) => {
|
||||||
queryClient.refetchQueries({
|
queryClient.refetchQueries({
|
||||||
queryKey: ['GET', endpoint],
|
queryKey: ['GET', endpoint],
|
||||||
force: true,
|
|
||||||
});
|
});
|
||||||
|
endpoint === 'api/linkOrder' &&
|
||||||
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/links'] });
|
||||||
|
endpoint === 'api/categoryOrder' &&
|
||||||
|
queryClient.refetchQueries({ queryKey: ['GET', 'api/categories'] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,22 +50,24 @@ function Links({ contentType }: any) {
|
|||||||
gridTemplateRows: 'auto',
|
gridTemplateRows: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{groups.map((category: any) => {
|
{groups
|
||||||
if (
|
.sort((a: any, b: any) => (a.categoryIndex > b.categoryIndex ? 1 : -1))
|
||||||
contentType.includes(category.filterName) ||
|
.map((category: any) => {
|
||||||
contentType.includes('All')
|
if (
|
||||||
) {
|
contentType.includes(category.filterName) ||
|
||||||
return (
|
contentType.includes('All')
|
||||||
<LinkGroups
|
) {
|
||||||
key={category.categoryName}
|
return (
|
||||||
category={category.categoryName}
|
<LinkGroups
|
||||||
links={bookmarkData.filter(
|
key={category.categoryName}
|
||||||
(bookmark: any) => bookmark.category === category.categoryName,
|
category={category.categoryName}
|
||||||
)}
|
links={bookmarkData.filter(
|
||||||
/>
|
(bookmark: any) => bookmark.category === category.categoryName,
|
||||||
);
|
)}
|
||||||
}
|
/>
|
||||||
})}
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ function LinkGroups({ links, category }: any) {
|
|||||||
<Text className={textClasses.linkCategory} classNames={textClasses}>
|
<Text className={textClasses.linkCategory} classNames={textClasses}>
|
||||||
{category}
|
{category}
|
||||||
</Text>
|
</Text>
|
||||||
{links?.map((link: any) => (
|
{links
|
||||||
<LinkItem key={link.name} link={link} />
|
?.sort((a: any, b: any) => (a.index > b.index ? 1 : -1))
|
||||||
))}
|
.map((link: any) => (
|
||||||
|
<LinkItem key={link.name} link={link} />
|
||||||
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
Divider,
|
Divider,
|
||||||
Group,
|
Group,
|
||||||
Highlight,
|
Highlight,
|
||||||
Modal,
|
Modal,
|
||||||
Popover,
|
Popover,
|
||||||
Stack,
|
Stack,
|
||||||
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
@@ -30,14 +34,43 @@ const BookmarkSettings = () => {
|
|||||||
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
||||||
endpoint: 'api/categories',
|
endpoint: 'api/categories',
|
||||||
});
|
});
|
||||||
const [groups, setGroups] = useState([]);
|
const [dragging, setDragging] = useState(false);
|
||||||
|
const [groups, setGroups] = useState<any>([]);
|
||||||
const [newGroup, setNewGroup] = useState<any>({});
|
const [newGroup, setNewGroup] = useState<any>({});
|
||||||
|
const [groupList, setGroupList] = useState<any>([]);
|
||||||
const { theme, isMobile } = useCommonHooks();
|
const { theme, isMobile } = useCommonHooks();
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
const [debouncedGroupSearch] = useDebouncedValue(searchValue, 500);
|
const [debouncedGroupSearch] = useDebouncedValue(searchValue, 500);
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(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(groups, result.source.index, result.destination.index);
|
||||||
|
setGroupList(
|
||||||
|
items.map((item: any, index: number) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (linkData && linksIsFetched) {
|
if (linkData && linksIsFetched) {
|
||||||
setBookmarks(linkData);
|
setBookmarks(linkData);
|
||||||
@@ -54,6 +87,12 @@ const BookmarkSettings = () => {
|
|||||||
}
|
}
|
||||||
}, [groupData, groupsIsFetched, debouncedGroupSearch]);
|
}, [groupData, groupsIsFetched, debouncedGroupSearch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (groupList.length > 0 && groupList !== groups) {
|
||||||
|
mutateAsync({ method: 'PATCH', endpoint: 'api/categoryOrder', body: groupList });
|
||||||
|
}
|
||||||
|
}, [groupList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Modal.Root
|
<Modal.Root
|
||||||
@@ -218,18 +257,117 @@ const BookmarkSettings = () => {
|
|||||||
setSearchValue(event.target.value);
|
setSearchValue(event.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Switch
|
||||||
|
label={'Reorder'}
|
||||||
|
labelPosition='left'
|
||||||
|
pos={'absolute'}
|
||||||
|
right={isMobile ? '2rem' : '1rem'}
|
||||||
|
top={isMobile ? '9.5rem' : undefined}
|
||||||
|
onChange={(e) => {
|
||||||
|
setDragging(e.target.checked);
|
||||||
|
}}
|
||||||
|
color={theme.primaryColor}
|
||||||
|
styles={{ label: { color: theme.primaryColor } }}
|
||||||
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
<Stack w={'100%'} align='center'>
|
{dragging ? (
|
||||||
<Group w={'75%'} align='start'>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
{groups?.map((group: any) => (
|
<Stack w={'100%'} align='center'>
|
||||||
<DebouncedGroupSettingsItem
|
<Droppable droppableId={'groupList'}>
|
||||||
key={group.id}
|
{(provided) => (
|
||||||
bookmarkGroup={group}
|
<Stack
|
||||||
bookmarks={bookmarks.filter((a: any) => a.category === group.categoryName)}
|
w={'75%'}
|
||||||
/>
|
align='center'
|
||||||
))}
|
justify='center'
|
||||||
</Group>
|
ref={provided.innerRef}
|
||||||
</Stack>
|
{...provided.droppableProps}
|
||||||
|
>
|
||||||
|
{groups
|
||||||
|
?.sort((a: any, b: any) =>
|
||||||
|
a.categoryIndex > b.categoryIndex ? 1 : -1,
|
||||||
|
)
|
||||||
|
.map((group: any) => (
|
||||||
|
<Draggable
|
||||||
|
key={group.id}
|
||||||
|
draggableId={`${group.id}`}
|
||||||
|
index={Number(group.categoryIndex)}
|
||||||
|
>
|
||||||
|
{(provided) => (
|
||||||
|
<Box
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
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,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{group.categoryName}
|
||||||
|
</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,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{group.filterName}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
{provided.placeholder}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</Stack>
|
||||||
|
</DragDropContext>
|
||||||
|
) : (
|
||||||
|
<Stack w={'100%'} align='center'>
|
||||||
|
<Group w={'75%'} align='start'>
|
||||||
|
{groups
|
||||||
|
?.sort((a: any, b: any) => (a.categoryIndex > b.categoryIndex ? 1 : -1))
|
||||||
|
.map((group: any) => (
|
||||||
|
<DebouncedGroupSettingsItem
|
||||||
|
key={group.id}
|
||||||
|
bookmarkGroup={group}
|
||||||
|
bookmarks={bookmarks
|
||||||
|
.filter((a: any) => a.category === group.categoryName)
|
||||||
|
.sort((a: any, b: any) => (a.index > b.index ? 1 : -1))}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Box,
|
Box,
|
||||||
@@ -29,12 +30,41 @@ function DebouncedGroupSettingsItem({ bookmarkGroup, bookmarks }: any) {
|
|||||||
const [update, setUpdate] = useState({});
|
const [update, setUpdate] = useState({});
|
||||||
const [debounced] = useDebouncedValue(update, 1000);
|
const [debounced] = useDebouncedValue(update, 1000);
|
||||||
const [newBookmark, setNewBookmark] = useState<any>({});
|
const [newBookmark, setNewBookmark] = useState<any>({});
|
||||||
|
const [bookmarkList, setBookmarkList] = useState<any>(bookmarks);
|
||||||
const handleDeleteBookmark = (application: any) => {
|
const handleDeleteBookmark = (application: any) => {
|
||||||
mutateAsync({ method: 'DELETE', endpoint: 'api/links', body: application });
|
mutateAsync({ method: 'DELETE', endpoint: 'api/links', body: application });
|
||||||
};
|
};
|
||||||
const handleDeleteGroup = (application: any) => {
|
const handleDeleteGroup = (application: any) => {
|
||||||
mutateAsync({ method: 'DELETE', endpoint: 'api/categories', body: application });
|
mutateAsync({ method: 'DELETE', endpoint: 'api/categories', body: application });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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(bookmarkList, result.source.index, result.destination.index);
|
||||||
|
setBookmarkList(
|
||||||
|
items.map((item: any, index: number) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(debounced).length) {
|
if (Object.keys(debounced).length) {
|
||||||
mutateAsync({ method: 'PATCH', endpoint: 'api/categories', body: debounced });
|
mutateAsync({ method: 'PATCH', endpoint: 'api/categories', body: debounced });
|
||||||
@@ -48,6 +78,12 @@ function DebouncedGroupSettingsItem({ bookmarkGroup, bookmarks }: any) {
|
|||||||
}
|
}
|
||||||
}, [update]);
|
}, [update]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bookmarkList && bookmarkList !== bookmarks) {
|
||||||
|
mutateAsync({ method: 'PATCH', endpoint: 'api/linkOrder', body: bookmarkList });
|
||||||
|
}
|
||||||
|
}, [bookmarkList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card w={'400px'} className={cardClasses.settingsItem} classNames={cardClasses}>
|
<Card w={'400px'} className={cardClasses.settingsItem} classNames={cardClasses}>
|
||||||
<Modal.Root
|
<Modal.Root
|
||||||
@@ -194,30 +230,57 @@ function DebouncedGroupSettingsItem({ bookmarkGroup, bookmarks }: any) {
|
|||||||
</Card.Section>
|
</Card.Section>
|
||||||
<Card.Section mt={16} p={16}>
|
<Card.Section mt={16} p={16}>
|
||||||
<Stack>
|
<Stack>
|
||||||
{bookmarks.map((bookmark: any) => (
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<Popover key={bookmark.id} withArrow withOverlay>
|
<Droppable droppableId={`${bookmarkGroup.id}`}>
|
||||||
<Popover.Target>
|
{(provided) => (
|
||||||
<Box>
|
<Stack ref={provided.innerRef} {...provided.droppableProps}>
|
||||||
<BookmarkSettingsItem bookmark={bookmark} />
|
{bookmarks
|
||||||
</Box>
|
.sort((a: any, b: any) => (a.index > b.index ? 1 : -1))
|
||||||
</Popover.Target>
|
.map((bookmark: any) => (
|
||||||
<Popover.Dropdown
|
<Draggable
|
||||||
style={{
|
key={bookmark.id}
|
||||||
'--color-text-pri': theme.primaryColor,
|
draggableId={`${bookmark.id}`}
|
||||||
'--color-text-acc': theme.accentColor,
|
index={bookmark.index}
|
||||||
'--color-background': theme.backgroundColor,
|
>
|
||||||
'--color-border': theme.borderColor,
|
{(provided: any) => (
|
||||||
'--color-search-background': theme.searchColor,
|
<Box
|
||||||
'--image-background': `url(${theme.backgroundImage})`,
|
ref={provided.innerRef}
|
||||||
}}
|
{...provided.draggableProps}
|
||||||
>
|
{...provided.dragHandleProps}
|
||||||
<DebouncedBookmarkItem
|
>
|
||||||
bookmarkItem={bookmark}
|
<Popover withArrow withOverlay>
|
||||||
handleDelete={handleDeleteBookmark}
|
<Popover.Target>
|
||||||
/>
|
<Box>
|
||||||
</Popover.Dropdown>
|
<BookmarkSettingsItem bookmark={bookmark} />
|
||||||
</Popover>
|
</Box>
|
||||||
))}
|
</Popover.Target>
|
||||||
|
<Popover.Dropdown
|
||||||
|
style={{
|
||||||
|
'--color-text-pri': theme.primaryColor,
|
||||||
|
'--color-text-acc': theme.accentColor,
|
||||||
|
'--color-background': theme.backgroundColor,
|
||||||
|
'--color-border': theme.borderColor,
|
||||||
|
'--color-search-background': theme.searchColor,
|
||||||
|
'--image-background': `url(${theme.backgroundImage})`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DebouncedBookmarkItem
|
||||||
|
bookmarkItem={bookmark}
|
||||||
|
handleDelete={handleDeleteBookmark}
|
||||||
|
/>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
{provided.placeholder}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{provided.placeholder}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</DragDropContext>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card.Section>
|
</Card.Section>
|
||||||
<Card.Section p={8} bg={theme.searchColor} c={theme.backgroundColor}>
|
<Card.Section p={8} bg={theme.searchColor} c={theme.backgroundColor}>
|
||||||
|
|||||||
@@ -31,4 +31,4 @@
|
|||||||
to {
|
to {
|
||||||
stroke-dashoffset: 1000;
|
stroke-dashoffset: 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,4 @@
|
|||||||
color: var(--color-text-acc);
|
color: var(--color-text-acc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user