Type fixes

This commit is contained in:
Sean Edmunds
2026-01-08 16:12:13 -06:00
parent fd160ede88
commit 3307bcefc9
10 changed files with 212 additions and 165 deletions

View File

@@ -139,8 +139,6 @@ export function useMutationAPI() {
queryClient.refetchQueries({ queryKey: ['GET', 'api/providers/default'] });
endpoint === 'api/personalData' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/personalData'] });
queryClient.refetchQueries({ queryKey: ['GET', 'api/wolEnabled'] });
queryClient.refetchQueries({ queryKey: ['GET', 'api/guacamoleEnabled'] });
},
});
}

View File

@@ -9,7 +9,7 @@ function Applications({ contentType }: { contentType: string[] }) {
endpoint: 'api/enabledApplications',
});
const { appSort, appDensity, appScale, theme, textShadowStyle } = useCommonHooks();
const applications = applicationData
const applications: Application[] = applicationData
?.sort((a: Application, b: Application) => {
switch (appSort) {
case 'A>Z':

View File

@@ -13,12 +13,12 @@ function Bookmarks({ contentType }: { contentType: string[] }) {
});
const { bookmarkSort, bookmarkDensity, bookmarkScale, theme, textShadowStyle } =
useCommonHooks();
const groupList = groupData?.map((category: BookmarkGroup) => {
const groupList: BookmarkGroup[] = groupData?.map((category: BookmarkGroup) => {
return category;
});
const bookmarks = bookmarkData || [];
const groups = groupList
?.sort((a: BookmarkGroup, b: BookmarkGroup) => {
const bookmarks: Bookmark[] = bookmarkData || [];
const groups: BookmarkGroup[] = groupList
?.toSorted((a: BookmarkGroup, b: BookmarkGroup) => {
switch (bookmarkSort) {
case 'A>Z':
return a.categoryName.toLocaleLowerCase() > b.categoryName.toLocaleLowerCase()

View File

@@ -28,7 +28,7 @@ import type {
import RecommendedApplications from './components/RecommendedApplications';
import RecommendedBookmarks from './components/RecommendedBookmarks';
import RecommendedClients from './components/RecommendedClients';
import SearchSelectItem from './components/SearchSelectItem';
import RenderItem from './components/RenderItem';
export default function Search({
current,
@@ -47,9 +47,6 @@ export default function Search({
position: string;
personal: PersonalData;
}) {
const { ref, height } = useElementSize();
const allIcons: any = { ...fa6, ...vsc, ...si };
const searchEndpoint = 'api/searchHistory';
const {
isMobile,
urlPattern,
@@ -72,6 +69,12 @@ export default function Search({
defaultProviderIcon,
setDefaultProviderIcon,
} = useCommonHooks();
const { ref, height } = useElementSize();
const { mutateAsync } = useMutationAPI();
const allIcons: any = { ...fa6, ...vsc, ...si };
const Icon = allIcons[providerIcon];
const searchEndpoint = 'api/searchHistory';
const { data: applicationData } = useQueryAPI({
endpoint: 'api/enabledApplications',
});
@@ -94,8 +97,14 @@ export default function Search({
const { data: historyData, isFetched: historyDataIsFetched } = useQueryAPI({
endpoint: searchEndpoint,
});
const applications: Application[] = applicationData || [];
const filteredApplications = applications?.filter((app: Application) =>
const providers: Provider[] = providerData || [];
const guacamoleHost: string = guacamoleHostData?.[0]?.guacamoleHost;
const guacamoleClients: GuacamoleClient[] = guacamoleClientData || [];
const searchHistory: SearchHistoryItem[] = historyData || [];
const filteredApplications: Application[] = applications?.filter((app: Application) =>
app.name.toLowerCase().includes((searchValue as string).substring(1).toLowerCase()),
);
const bookmarks: Bookmark[] =
@@ -114,26 +123,23 @@ export default function Search({
bookmark.enabled === 1 &&
bookmark.name.toLowerCase().includes((searchValue as string).toLowerCase()),
) || [];
const providers = providerData || [];
const guacamoleHost = guacamoleHostData?.[0]?.guacamoleHost;
const guacamoleClients = guacamoleClientData || [];
const filteredGuacamoleClients =
const filteredGuacamoleClients: GuacamoleClient[] =
guacamoleClients.filter((client: GuacamoleClient) =>
client.clientName
.toLowerCase()
.includes((searchValue as string).substring(1).toLowerCase()),
) || [];
const searchHistory = historyData || [];
const [filteredHistory, setFilteredHistory] = useState<SearchHistoryItem[]>([]);
const { mutateAsync } = useMutationAPI();
const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
useDisclosure(false);
const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure();
const [listIndex, setListIndex] = useState(-1);
const [placeholderValue, setPlaceholderValue] = useState<string>('');
const [listIndex, setListIndex] = useState(-1);
const [value, setValue] = useState(0);
const [historyMultiplier, setHistoryMultiplier] = useState(1);
const Icon = allIcons[providerIcon];
const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure();
const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
useDisclosure(false);
const handleResetSearch = () => {
setSearchValue('');
@@ -143,21 +149,6 @@ export default function Search({
setProviderURL(defaultProviderURL);
};
const handleSearchHistoryItem = ({ option }: { option: SearchHistoryItem }) => {
const url =
providerData.find((provider: Provider) => provider.prefix === option.prefix)?.url ??
defaultProviderURL;
open(`${url}${option.searchTerm}`, '_self');
};
const handleAppLaunchItem = ({ option }: { option: any }) => {
const validUrl =
option.url.startsWith('http://') || option.url.startsWith('https://')
? option.url
: `http://${option.url}`;
open(validUrl, '_self');
};
const handleGuacamoleClientLaunchItem = ({ option }: { option: any }) => {
const validUrl = `${guacamoleHost}/#/client/${option.clientIdentifier}`;
open(validUrl, '_self');
@@ -324,7 +315,7 @@ export default function Search({
setProviderPrefix('/g');
setProviderIcon('FaGoogle');
} else if (item.target.value.startsWith('/')) {
const provider = providers?.find(
const provider: Provider | undefined = providers?.find(
(p: Provider) => p.prefix === item.target.value.split(' ')[0],
);
setProviderLabel(provider?.name ?? 'Invalid Search');
@@ -434,112 +425,6 @@ export default function Search({
return historyOptions;
};
const renderItem: any = (item: any) => {
const Icon = item.option.prefix
? allIcons[
item.option.prefix === '/g'
? 'FaGoogle'
: (providers.find((p: Provider) => p.prefix === item.option.prefix)?.icon ??
'')
]
: item.option.icon
? allIcons[item.option.icon]
: item.option.type && item.option.type === 'SSH'
? allIcons.FaTerminal
: item.option.type === 'RDP'
? allIcons.FaComputer
: allIcons.FaDesktop;
return (searchValue as string).startsWith('!') ? (
<Group
pos={'relative'}
m={0}
p={0}
gap={0}
justify='center'
w={'100%'}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleAppLaunchItem(item);
}}
>
<Text pos={'absolute'} left={'1rem'} c={'var(--mantine-color-accent-0)'}>
Launch
</Text>
{Icon && (
<Icon
style={{
color: 'var(--mantine-color-primary-0)',
height: '2rem',
width: '2rem',
}}
/>
)}
<Text pos={'absolute'} right={'1rem'} c={'var(--mantine-color-accent-0)'}>
{item.option.name}
</Text>
</Group>
) : (searchValue as string).startsWith('>') && personal.enableGuacamole === 1 ? (
<Group
pos={'relative'}
m={0}
p={0}
gap={0}
justify='center'
w={'100%'}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleGuacamoleClientLaunchItem(item);
}}
>
<Text pos={'absolute'} left={'1rem'} c={'var(--mantine-color-accent-0)'}>
Connect to
</Text>
<Icon
style={{
color: 'var(--mantine-color-primary-0)',
height: '2rem',
width: '2rem',
}}
/>
<Text pos={'absolute'} right={'1rem'} c={'var(--mantine-color-accent-0)'}>
{item.option.name || item.option.clientName || 'Invalid connection'}
</Text>
</Group>
) : item.option.value === 'load' ? (
<Group
pos={'relative'}
m={0}
p={0}
gap={0}
w={'100%'}
h={'2rem'}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setHistoryMultiplier((prev: number) => prev + 1);
}}
>
<Text
w={'100%'}
c={'var(--mantine-color-accent-0)'}
style={{ textAlign: 'center' }}
>
{item.option.label}
</Text>
</Group>
) : (
<SearchSelectItem
item={item}
handleSearchHistoryItem={handleSearchHistoryItem}
searchEndpoint={searchEndpoint}
setSearchValue={setSearchValue}
Icon={Icon}
/>
);
};
useEffect(() => {
if (historyData && historyDataIsFetched) {
if (filteredHistory.length === 0) {
@@ -580,6 +465,7 @@ export default function Search({
setDefaultProviderIcon(defaultProviderData[0]?.icon ?? 'FaGoogle');
}
}, [defaultProviderData, defaultProviderDataIsFetched]);
return (
<>
{dropdownOpened && <Overlay blur={5} onClick={closeDropdown} fixed zIndex={200} />}
@@ -647,7 +533,19 @@ export default function Search({
bg: 'transparent',
}}
scrollAreaProps={{ viewportRef: ref }}
renderOption={(item: any) => renderItem(item)}
renderOption={(item: any) => (
<RenderItem
item={item}
allIcons={allIcons}
providers={providers}
searchValue={searchValue}
setSearchValue={setSearchValue}
personal={personal}
handleGuacamoleClientLaunchItem={handleGuacamoleClientLaunchItem}
setHistoryMultiplier={setHistoryMultiplier}
searchEndpoint={searchEndpoint}
/>
)}
onFocus={() => {
if (searchValue !== '') {
openDropdown();
@@ -703,17 +601,22 @@ export default function Search({
</Text>
<Group align='start' justify='space-between'>
<Stack w={'49%'} gap={8}>
{bookmarks.length > 0 && (
<RecommendedBookmarks
bookmarks={bookmarks}
searchValue={searchValue}
dropdownOpened={dropdownOpened}
/>
)}
{personal.enableGuacamole === 1 &&
filteredGuacamoleClients.length > 0 && (
<RecommendedClients
filteredGuacamoleClients={filteredGuacamoleClients}
searchValue={searchValue}
dropdownOpened={dropdownOpened}
handleGuacamoleClientLaunchItem={handleGuacamoleClientLaunchItem}
/>
)}
</Stack>
<RecommendedApplications
searchValue={searchValue}

View File

@@ -0,0 +1,144 @@
import { Group, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import type { PersonalData, Provider, SearchHistoryItem } from 'types';
import SearchSelectItem from './SearchSelectItem';
export default function RenderItem({
item,
allIcons,
providers,
searchValue,
setSearchValue,
personal,
handleGuacamoleClientLaunchItem,
setHistoryMultiplier,
searchEndpoint,
}: {
item: any;
allIcons: any;
providers: Provider[];
searchValue: string | number;
setSearchValue: React.Dispatch<React.SetStateAction<string | number>>;
personal: PersonalData;
handleGuacamoleClientLaunchItem: ({ option }: { option: any }) => void;
setHistoryMultiplier: React.Dispatch<React.SetStateAction<number>>;
searchEndpoint: string;
}) {
const { defaultProviderURL } = useCommonHooks();
const handleSearchHistoryItem = ({ option }: { option: SearchHistoryItem }) => {
const url =
providers.find((provider: Provider) => provider.prefix === option.prefix)?.url ??
defaultProviderURL;
open(`${url}${option.searchTerm}`, '_self');
};
const handleAppLaunchItem = ({ option }: { option: any }) => {
const validUrl =
option.url.startsWith('http://') || option.url.startsWith('https://')
? option.url
: `http://${option.url}`;
open(validUrl, '_self');
};
const Icon = item.option.prefix
? allIcons[
item.option.prefix === '/g'
? 'FaGoogle'
: (providers.find((p: Provider) => p.prefix === item.option.prefix)?.icon ?? '')
]
: item.option.icon
? allIcons[item.option.icon]
: item.option.type && item.option.type === 'SSH'
? allIcons.FaTerminal
: item.option.type === 'RDP'
? allIcons.FaComputer
: allIcons.FaDesktop;
return (searchValue as string).startsWith('!') ? (
<Group
pos={'relative'}
m={0}
p={0}
gap={0}
justify='center'
w={'100%'}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleAppLaunchItem(item);
}}
>
<Text pos={'absolute'} left={'1rem'} c={'var(--mantine-color-accent-0)'}>
Launch
</Text>
{Icon && (
<Icon
style={{
color: 'var(--mantine-color-primary-0)',
height: '2rem',
width: '2rem',
}}
/>
)}
<Text pos={'absolute'} right={'1rem'} c={'var(--mantine-color-accent-0)'}>
{item.option.name}
</Text>
</Group>
) : (searchValue as string).startsWith('>') && personal.enableGuacamole === 1 ? (
<Group
pos={'relative'}
m={0}
p={0}
gap={0}
justify='center'
w={'100%'}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleGuacamoleClientLaunchItem(item);
}}
>
<Text pos={'absolute'} left={'1rem'} c={'var(--mantine-color-accent-0)'}>
Connect to
</Text>
<Icon
style={{
color: 'var(--mantine-color-primary-0)',
height: '2rem',
width: '2rem',
}}
/>
<Text pos={'absolute'} right={'1rem'} c={'var(--mantine-color-accent-0)'}>
{item.option.name || item.option.clientName || 'Invalid connection'}
</Text>
</Group>
) : item.option.value === 'load' ? (
<Group
pos={'relative'}
m={0}
p={0}
gap={0}
w={'100%'}
h={'2rem'}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setHistoryMultiplier((prev: number) => prev + 1);
}}
>
<Text
w={'100%'}
c={'var(--mantine-color-accent-0)'}
style={{ textAlign: 'center' }}
>
{item.option.label}
</Text>
</Group>
) : (
<SearchSelectItem
item={item}
handleSearchHistoryItem={handleSearchHistoryItem}
searchEndpoint={searchEndpoint}
setSearchValue={setSearchValue}
Icon={Icon}
/>
);
}

View File

@@ -19,7 +19,7 @@ function SettingsButton() {
const { data: wolEnabledData, isFetched: wolEnabledDataIsFetched } = useQueryAPI({
endpoint: 'api/wolEnabled',
});
const wolClients = wolClientData || [];
const wolClients: WolClient[] = wolClientData || [];
const [enabled, setEnabled] = useState<number>(0);
const { mutateAsync } = useMutationAPI();
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);

View File

@@ -36,7 +36,7 @@ const BookmarkSettings = () => {
const { data: bookmarkData, isFetching: bookmarkDataIsFetching } = useQueryAPI({
endpoint: bookmarksEndpoint,
});
const bookmarks = bookmarkData || [];
const bookmarks: Bookmark[] = bookmarkData || [];
const categoriesEndpoint = 'api/bookmarkCategories';
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
endpoint: categoriesEndpoint,
@@ -50,7 +50,7 @@ const BookmarkSettings = () => {
const [debouncedGroupSearch] = useDebouncedValue(searchValue, 500);
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const [isLoading, setIsLoading] = useState(false);
const foundBookmarkGroups = [
const foundBookmarkGroups: BookmarkGroup[] = [
...new Set(
bookmarks
.filter(
@@ -68,7 +68,7 @@ const BookmarkSettings = () => {
),
),
];
const foundGroups = groupData.filter((a: BookmarkGroup) =>
const foundGroups: BookmarkGroup[] = groupData.filter((a: BookmarkGroup) =>
a.categoryName.toLowerCase().includes((debouncedGroupSearch as string).toLowerCase()),
);
const handleClose = () => {

View File

@@ -46,7 +46,7 @@ const GuacamoleClients = () => {
useQueryAPI({
endpoint: endpoint,
});
const guacamoleClients =
const guacamoleClients: GuacamoleClient[] =
guacamoleClientsData.filter(
(a: GuacamoleClient) =>
a.clientName.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||

View File

@@ -33,7 +33,7 @@ const MessageSettings = () => {
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
const { isMobile, theme } = useCommonHooks();
const { mutateAsync } = useMutationAPI();
const messages =
const messages: Message[] =
messageData?.filter((m: Message) =>
m.message.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
) || [];

View File

@@ -36,12 +36,14 @@ import SearchProviderSettingsItem from './SearchProviderSettingsItem';
const SearchProviderSettings = () => {
const allIcons: any = { ...fa6, ...vsc, ...si };
const defaultProvider = {
const defaultProvider: Provider = {
id: undefined,
name: '',
url: '',
prefix: '',
default: 0,
enabled: 1,
icon: null,
};
const form = useForm({
initialValues: defaultProvider,
@@ -57,7 +59,7 @@ const SearchProviderSettings = () => {
});
const [searchValue, setSearchValue] = useState<string | number>('');
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
const providers =
const providers: Provider[] =
providerData
.filter(
(a: Provider) =>