import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'; import { Box, Button, type ComboboxItem, type ComboboxLikeRenderOptionInput, Grid, GridCol, Group, Popover, Select, Stack, Text, TextInput, } from '@mantine/core'; import { isNotEmpty, useForm } from '@mantine/form'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { AppItem } from 'components'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; import * as fa6 from 'react-icons/fa6'; import * as si from 'react-icons/si'; import * as vsc from 'react-icons/vsc'; import { popoverClasses, selectClasses, textInputClasses } from 'styles'; import type { Application } from 'types'; import AddItemModal from '../Shared/AddItemModal'; import NoItemsFound from '../Shared/NoItemsFound'; import SettingsHeader from '../Shared/SettingsHeader'; import DebouncedApplication from './DebouncedApplication'; const ApplicationSettings = () => { const allIcons: any = { ...fa6, ...vsc, ...si }; const defaultApplication: Application = { id: null, name: '', url: '', icon: '', baseUrl: '', filterName: 'Default', index: 0, enabled: 1, }; const form = useForm({ initialValues: defaultApplication, validate: { name: isNotEmpty('Name is required'), url: isNotEmpty('URL is required'), icon: isNotEmpty('Icon is required'), filterName: isNotEmpty('Filter name is required'), }, }); const appsEndpoint = 'api/applications'; const appsOrderEndpoint = 'api/applicationOrder'; const { data: applicationData, isFetching: isAppsFetching } = useQueryAPI({ endpoint: appsEndpoint, }); const [applications, setApplications] = useState([]); const [applicationList, setApplicationList] = useState([]); const { isMobile, reorder } = useCommonHooks(); const { mutateAsync } = useMutationAPI(); const [searchValue, setSearchValue] = useState(''); const [debouncedSearch] = useDebouncedValue(searchValue, 500); const [dragging, setDragging] = useState(false); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); function onDragEnd(result: any) { if (!result.destination) { return; } if (result.destination.index === result.source.index) { return; } const items = reorder(applications, 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 handleClose = () => { closeModal(); form.reset(); }; useEffect(() => { if (applicationData && !isAppsFetching) { setApplications( applicationData .filter( (a: Application) => a.name.toLowerCase().includes(debouncedSearch.toLowerCase()) || a.url.toLowerCase().includes(debouncedSearch.toLowerCase()) || a.baseUrl.toLowerCase().includes(debouncedSearch.toLowerCase()), ) .sort((a: Application, b: Application) => (a.index > b.index ? 1 : -1)), ); } if (debouncedSearch === searchValue) { setIsLoading(false); } }, [applicationData, isAppsFetching, debouncedSearch]); useEffect(() => { if (applicationList.length > 0 && applicationList !== applications) { mutateAsync({ method: 'PATCH', endpoint: appsOrderEndpoint, body: applicationList, }); } }, [applicationList]); return ( 0 ? setDragging : undefined} openModal={openModal} title={'Applications'} tooltip={{ title: 'Applications are the app buttons that will be displayed on the homepage.', highlightList: [ 'Applications', 'applicationData', 'application', 'app', 'apps', 'name', 'base url', 'url', 'icon', 'filter', ], description: 'Applications contain a name, base url, url, icon and filter. URLs can contain either http(s):// or not. Base URLs should start with /, otherwise they will cause errors.', }} searchValue={searchValue} setSearchValue={setSearchValue} searchPlaceholder={'Search Applications...'} isLoading={isLoading} setIsLoading={setIsLoading} /> { mutateAsync({ method: 'POST', endpoint: appsEndpoint, body: { ...values, index: applications.length, }, }); handleClose(); })} >