Search bar fixes and performance updates
This commit is contained in:
24
package-lock.json
generated
24
package-lock.json
generated
@@ -24,7 +24,7 @@
|
|||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router-dom": "^7.11.0",
|
"react-router-dom": "^7.12.0",
|
||||||
"recharts": "^3.6.0"
|
"recharts": "^3.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"@vitest/coverage-v8": "^4.0.16",
|
"@vitest/coverage-v8": "^4.0.16",
|
||||||
"@vitest/ui": "^4.0.16",
|
"@vitest/ui": "^4.0.16",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.0",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-dts": "~4.5.4",
|
"vite-plugin-dts": "~4.5.4",
|
||||||
"vite-tsconfig-paths": "^6.0.3",
|
"vite-tsconfig-paths": "^6.0.3",
|
||||||
"vitest": "^4.0.16"
|
"vitest": "^4.0.16"
|
||||||
@@ -3486,9 +3486,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "7.11.0",
|
"version": "7.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz",
|
||||||
"integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==",
|
"integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "^1.0.1",
|
"cookie": "^1.0.1",
|
||||||
"set-cookie-parser": "^2.6.0"
|
"set-cookie-parser": "^2.6.0"
|
||||||
@@ -3507,11 +3507,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "7.11.0",
|
"version": "7.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.12.0.tgz",
|
||||||
"integrity": "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g==",
|
"integrity": "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-router": "7.11.0"
|
"react-router": "7.12.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
@@ -4110,9 +4110,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "triggerfrontend",
|
"name": "triggerfrontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router-dom": "^7.11.0",
|
"react-router-dom": "^7.12.0",
|
||||||
"recharts": "^3.6.0"
|
"recharts": "^3.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"@vitest/coverage-v8": "^4.0.16",
|
"@vitest/coverage-v8": "^4.0.16",
|
||||||
"@vitest/ui": "^4.0.16",
|
"@vitest/ui": "^4.0.16",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.0",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-dts": "~4.5.4",
|
"vite-plugin-dts": "~4.5.4",
|
||||||
"vite-tsconfig-paths": "^6.0.3",
|
"vite-tsconfig-paths": "^6.0.3",
|
||||||
"vitest": "^4.0.16"
|
"vitest": "^4.0.16"
|
||||||
|
|||||||
@@ -41,11 +41,7 @@ export function Homepage() {
|
|||||||
const { data: personalData, isFetched: personalIsFetched } = useQueryAPI({
|
const { data: personalData, isFetched: personalIsFetched } = useQueryAPI({
|
||||||
endpoint: 'api/personalData',
|
endpoint: 'api/personalData',
|
||||||
});
|
});
|
||||||
const {
|
const { data: messageData, isFetching: isMessagesFetching } = useQueryAPI({
|
||||||
data: messageData,
|
|
||||||
isFetching: isMessagesFetching,
|
|
||||||
isFetched: isMessagesFetched,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: 'api/displayedGreeting',
|
endpoint: 'api/displayedGreeting',
|
||||||
});
|
});
|
||||||
const { data: weatherMetadata } = useOSMAPI({
|
const { data: weatherMetadata } = useOSMAPI({
|
||||||
@@ -80,8 +76,8 @@ export function Homepage() {
|
|||||||
y: points?.properties.gridY ?? currentLocation.y,
|
y: points?.properties.gridY ?? currentLocation.y,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [personal, setPersonal] = useState<PersonalData>();
|
const personal: PersonalData = personalData?.[0];
|
||||||
const [message, setMessage] = useState<Message>();
|
const message: Message = messageData?.[0];
|
||||||
const [displayedGreeting, setDisplayedGreeting] = useState<string | undefined>('');
|
const [displayedGreeting, setDisplayedGreeting] = useState<string | undefined>('');
|
||||||
const [current, setCurrent] = useState<CW>({
|
const [current, setCurrent] = useState<CW>({
|
||||||
icon: '',
|
icon: '',
|
||||||
@@ -110,15 +106,9 @@ export function Homepage() {
|
|||||||
const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
|
const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
|
||||||
useDisclosure(false);
|
useDisclosure(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (messageData && isMessagesFetched) {
|
|
||||||
setMessage(messageData[0]);
|
|
||||||
}
|
|
||||||
}, [messageData, isMessagesFetched]);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (personalData && personalIsFetched) {
|
if (personalData && personalIsFetched) {
|
||||||
setCurrentUser(personalData[0]);
|
setCurrentUser(personalData[0]);
|
||||||
setPersonal(personalData[0]);
|
|
||||||
}
|
}
|
||||||
}, [personalData, personalIsFetched]);
|
}, [personalData, personalIsFetched]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -220,7 +210,7 @@ export function Homepage() {
|
|||||||
closeWeatherModal={closeWeatherModal}
|
closeWeatherModal={closeWeatherModal}
|
||||||
openWeatherModal={openWeatherModal}
|
openWeatherModal={openWeatherModal}
|
||||||
position='top'
|
position='top'
|
||||||
personal={personal as PersonalData}
|
personal={personal}
|
||||||
/>
|
/>
|
||||||
<Header
|
<Header
|
||||||
greet={displayedGreeting}
|
greet={displayedGreeting}
|
||||||
@@ -246,7 +236,7 @@ export function Homepage() {
|
|||||||
closeWeatherModal={closeWeatherModal}
|
closeWeatherModal={closeWeatherModal}
|
||||||
openWeatherModal={openWeatherModal}
|
openWeatherModal={openWeatherModal}
|
||||||
position='center'
|
position='center'
|
||||||
personal={personal as PersonalData}
|
personal={personal}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
import { Stack, Text } from '@mantine/core';
|
import { Stack, Text } from '@mantine/core';
|
||||||
import { AppItem } from 'components';
|
import { AppItem } from 'components';
|
||||||
import { useCommonHooks, useQueryAPI } from 'hooks';
|
import { useCommonHooks, useQueryAPI } from 'hooks';
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { textClasses } from 'styles';
|
import { textClasses } from 'styles';
|
||||||
import type { Application } from 'types';
|
import type { Application } from 'types';
|
||||||
|
|
||||||
function Applications({ contentType }: { contentType: string[] }) {
|
function Applications({ contentType }: { contentType: string[] }) {
|
||||||
const { data: applicationData, isFetching: isApplicationDataFetching } = useQueryAPI({
|
const { data: applicationData } = useQueryAPI({
|
||||||
endpoint: 'api/enabledApplications',
|
endpoint: 'api/enabledApplications',
|
||||||
});
|
});
|
||||||
const { appSort, appDensity, appScale, theme, textShadowStyle } = useCommonHooks();
|
const { appSort, appDensity, appScale, theme, textShadowStyle } = useCommonHooks();
|
||||||
const [applications, setApplications] = useState<Application[]>([]);
|
const applications = applicationData
|
||||||
|
?.sort((a: Application, b: Application) => {
|
||||||
useEffect(() => {
|
|
||||||
if (applicationData && !isApplicationDataFetching) {
|
|
||||||
setApplications(
|
|
||||||
applicationData
|
|
||||||
.sort((a: Application, b: Application) => {
|
|
||||||
switch (appSort) {
|
switch (appSort) {
|
||||||
case 'A>Z':
|
case 'A>Z':
|
||||||
return a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase() ? 1 : -1;
|
return a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase() ? 1 : -1;
|
||||||
@@ -41,14 +35,11 @@ function Applications({ contentType }: { contentType: string[] }) {
|
|||||||
?.filter(
|
?.filter(
|
||||||
(application: Application) =>
|
(application: Application) =>
|
||||||
contentType.includes(application.filterName) || contentType.includes('All'),
|
contentType.includes(application.filterName) || contentType.includes('All'),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}, [applicationData, isApplicationDataFetching, appSort, contentType]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{applications.length > 0 && contentType.length > 0 && (
|
{applications?.length > 0 && contentType.length > 0 && (
|
||||||
<Stack gap={0}>
|
<Stack gap={0}>
|
||||||
<Text
|
<Text
|
||||||
className={textClasses.sectionTitle}
|
className={textClasses.sectionTitle}
|
||||||
@@ -66,7 +57,7 @@ function Applications({ contentType }: { contentType: string[] }) {
|
|||||||
gridTemplateColumns: `repeat(${appDensity}, 1fr)`,
|
gridTemplateColumns: `repeat(${appDensity}, 1fr)`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{applications.map((application: Application) => {
|
{applications?.map((application: Application) => {
|
||||||
return (
|
return (
|
||||||
<AppItem
|
<AppItem
|
||||||
key={application.name}
|
key={application.name}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Stack, Text } from '@mantine/core';
|
import { Stack, Text } from '@mantine/core';
|
||||||
import { useCommonHooks, useQueryAPI } from 'hooks';
|
import { useCommonHooks, useQueryAPI } from 'hooks';
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { textClasses } from 'styles';
|
import { textClasses } from 'styles';
|
||||||
import type { Bookmark, BookmarkGroup } from 'types';
|
import type { Bookmark, BookmarkGroup } from 'types';
|
||||||
import BookmarkGroups from './BookmarkGroups';
|
import BookmarkGroups from './BookmarkGroups';
|
||||||
@@ -14,32 +13,19 @@ function Bookmarks({ contentType }: { contentType: string[] }) {
|
|||||||
});
|
});
|
||||||
const { bookmarkSort, bookmarkDensity, bookmarkScale, theme, textShadowStyle } =
|
const { bookmarkSort, bookmarkDensity, bookmarkScale, theme, textShadowStyle } =
|
||||||
useCommonHooks();
|
useCommonHooks();
|
||||||
const [bookmarks, setBookmarks] = useState<Bookmark[]>([]);
|
|
||||||
const [groups, setGroups] = useState<BookmarkGroup[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (bookmarkData && bookmarksIsFetched) {
|
|
||||||
setBookmarks(bookmarkData);
|
|
||||||
}
|
|
||||||
}, [bookmarksIsFetched, bookmarkData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (groupData && groupsIsFetched) {
|
|
||||||
const groupList = groupData?.map((category: BookmarkGroup) => {
|
const groupList = groupData?.map((category: BookmarkGroup) => {
|
||||||
return category;
|
return category;
|
||||||
});
|
});
|
||||||
setGroups(
|
const bookmarks = bookmarkData || [];
|
||||||
groupList
|
const groups = groupList
|
||||||
.sort((a: BookmarkGroup, b: BookmarkGroup) => {
|
?.sort((a: BookmarkGroup, b: BookmarkGroup) => {
|
||||||
switch (bookmarkSort) {
|
switch (bookmarkSort) {
|
||||||
case 'A>Z':
|
case 'A>Z':
|
||||||
return a.categoryName.toLocaleLowerCase() >
|
return a.categoryName.toLocaleLowerCase() > b.categoryName.toLocaleLowerCase()
|
||||||
b.categoryName.toLocaleLowerCase()
|
|
||||||
? 1
|
? 1
|
||||||
: -1;
|
: -1;
|
||||||
case 'Z>A':
|
case 'Z>A':
|
||||||
return a.categoryName.toLocaleLowerCase() <
|
return a.categoryName.toLocaleLowerCase() < b.categoryName.toLocaleLowerCase()
|
||||||
b.categoryName.toLocaleLowerCase()
|
|
||||||
? 1
|
? 1
|
||||||
: -1;
|
: -1;
|
||||||
case 'Newest':
|
case 'Newest':
|
||||||
@@ -61,10 +47,7 @@ function Bookmarks({ contentType }: { contentType: string[] }) {
|
|||||||
?.filter(
|
?.filter(
|
||||||
(category: BookmarkGroup) =>
|
(category: BookmarkGroup) =>
|
||||||
contentType.includes(category.filterName) || contentType.includes('All'),
|
contentType.includes(category.filterName) || contentType.includes('All'),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}, [groupData, groupsIsFetched, bookmarkSort, contentType]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -49,46 +49,7 @@ export default function Search({
|
|||||||
}) {
|
}) {
|
||||||
const { ref, height } = useElementSize();
|
const { ref, height } = useElementSize();
|
||||||
const allIcons: any = { ...fa6, ...vsc, ...si };
|
const allIcons: any = { ...fa6, ...vsc, ...si };
|
||||||
const { data: providerData, isFetched: providerDataIsFetched } = useQueryAPI({
|
|
||||||
endpoint: 'api/providers',
|
|
||||||
});
|
|
||||||
const { data: applicationData, isFetching: isApplicationDataFetching } = useQueryAPI({
|
|
||||||
endpoint: 'api/enabledApplications',
|
|
||||||
});
|
|
||||||
const { data: guacamoleHostData, isFetched: guacamoleHostDataIsFetched } = useQueryAPI({
|
|
||||||
endpoint: 'api/guacamoleHost',
|
|
||||||
});
|
|
||||||
const { data: guacamoleClientData, isFetched: guacamoleClientDataIsFetched } =
|
|
||||||
useQueryAPI({
|
|
||||||
endpoint: 'api/guacamoleClients',
|
|
||||||
});
|
|
||||||
const { data: bookmarkData, isFetched: bookmarksIsFetched } = useQueryAPI({
|
|
||||||
endpoint: 'api/bookmarks',
|
|
||||||
});
|
|
||||||
const { data: defaultProviderData, isFetched: defaultProviderDataIsFetched } =
|
|
||||||
useQueryAPI({
|
|
||||||
endpoint: 'api/providers/default',
|
|
||||||
});
|
|
||||||
const [providers, setProviders] = useState<Provider[]>([]);
|
|
||||||
const [guacamoleHost, setGuacamoleHost] = useState<string | undefined>('');
|
|
||||||
const [guacamoleClients, setGuacamoleClients] = useState<GuacamoleClient[]>([]);
|
|
||||||
const [filteredGuacamoleClients, setFilteredGuacamoleClients] = useState<
|
|
||||||
GuacamoleClient[]
|
|
||||||
>([]);
|
|
||||||
const [applications, setApplications] = useState<Application[]>([]);
|
|
||||||
const [filteredApplications, setFilteredApplications] = useState<Application[]>([]);
|
|
||||||
const [bookmarks, setBookmarks] = useState<Bookmark[]>([]);
|
|
||||||
const searchEndpoint = 'api/searchHistory';
|
const searchEndpoint = 'api/searchHistory';
|
||||||
const { data: historyData, isFetched: historyDataIsFetched } = useQueryAPI({
|
|
||||||
endpoint: searchEndpoint,
|
|
||||||
});
|
|
||||||
const [searchHistory, setSearchHistory] = useState<SearchHistoryItem[]>([]);
|
|
||||||
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 {
|
const {
|
||||||
isMobile,
|
isMobile,
|
||||||
urlPattern,
|
urlPattern,
|
||||||
@@ -111,6 +72,64 @@ export default function Search({
|
|||||||
defaultProviderIcon,
|
defaultProviderIcon,
|
||||||
setDefaultProviderIcon,
|
setDefaultProviderIcon,
|
||||||
} = useCommonHooks();
|
} = useCommonHooks();
|
||||||
|
const { data: applicationData } = useQueryAPI({
|
||||||
|
endpoint: 'api/enabledApplications',
|
||||||
|
});
|
||||||
|
const { data: bookmarkData } = useQueryAPI({
|
||||||
|
endpoint: 'api/bookmarks',
|
||||||
|
});
|
||||||
|
const { data: guacamoleHostData } = useQueryAPI({
|
||||||
|
endpoint: 'api/guacamoleHost',
|
||||||
|
});
|
||||||
|
const { data: guacamoleClientData } = useQueryAPI({
|
||||||
|
endpoint: 'api/guacamoleClients',
|
||||||
|
});
|
||||||
|
const { data: providerData } = useQueryAPI({
|
||||||
|
endpoint: 'api/providers',
|
||||||
|
});
|
||||||
|
const { data: defaultProviderData, isFetched: defaultProviderDataIsFetched } =
|
||||||
|
useQueryAPI({
|
||||||
|
endpoint: 'api/providers/default',
|
||||||
|
});
|
||||||
|
const { data: historyData, isFetched: historyDataIsFetched } = useQueryAPI({
|
||||||
|
endpoint: searchEndpoint,
|
||||||
|
});
|
||||||
|
const applications: Application[] = applicationData || [];
|
||||||
|
const filteredApplications = applications?.filter((app: Application) =>
|
||||||
|
app.name.toLowerCase().includes((searchValue as string).substring(1).toLowerCase()),
|
||||||
|
);
|
||||||
|
const bookmarks: Bookmark[] =
|
||||||
|
(searchValue as string).startsWith('>') ||
|
||||||
|
(searchValue as string).startsWith('!') ||
|
||||||
|
(searchValue as string).startsWith('/')
|
||||||
|
? bookmarkData?.filter(
|
||||||
|
(bookmark: Bookmark) =>
|
||||||
|
bookmark.enabled === 1 &&
|
||||||
|
bookmark.name
|
||||||
|
.toLowerCase()
|
||||||
|
.includes((searchValue as string).substring(1).toLowerCase()),
|
||||||
|
)
|
||||||
|
: bookmarkData?.filter(
|
||||||
|
(bookmark: Bookmark) =>
|
||||||
|
bookmark.enabled === 1 &&
|
||||||
|
bookmark.name.toLowerCase().includes((searchValue as string).toLowerCase()),
|
||||||
|
) || [];
|
||||||
|
const providers = providerData || [];
|
||||||
|
const guacamoleHost = guacamoleHostData?.[0]?.guacamoleHost;
|
||||||
|
const guacamoleClients = guacamoleClientData || [];
|
||||||
|
const filteredGuacamoleClients =
|
||||||
|
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 [placeholderValue, setPlaceholderValue] = useState<string>('');
|
||||||
const [value, setValue] = useState(0);
|
const [value, setValue] = useState(0);
|
||||||
const [historyMultiplier, setHistoryMultiplier] = useState(1);
|
const [historyMultiplier, setHistoryMultiplier] = useState(1);
|
||||||
@@ -155,7 +174,7 @@ export default function Search({
|
|||||||
} else {
|
} else {
|
||||||
subtext = item.target.value;
|
subtext = item.target.value;
|
||||||
}
|
}
|
||||||
if (item.key === 'Enter') {
|
if (item.code === 'Enter') {
|
||||||
if (providerLabel === 'Show help') {
|
if (providerLabel === 'Show help') {
|
||||||
setSearchValue('');
|
setSearchValue('');
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
@@ -171,9 +190,11 @@ export default function Search({
|
|||||||
} else if (providerLabel === 'Show weather') {
|
} else if (providerLabel === 'Show weather') {
|
||||||
openWeatherModal();
|
openWeatherModal();
|
||||||
} else if (providerLabel.startsWith('Launch')) {
|
} else if (providerLabel.startsWith('Launch')) {
|
||||||
const url = applications.find(
|
const url = applications?.find((app: Application) =>
|
||||||
(app: Application) =>
|
app.baseUrl
|
||||||
app.baseUrl.substring(1).toLowerCase() === item.target.value.substr(1),
|
.substring(1)
|
||||||
|
.toLowerCase()
|
||||||
|
.startsWith(item.target.value.substr(1).toLowerCase()),
|
||||||
)?.url;
|
)?.url;
|
||||||
const validUrl =
|
const validUrl =
|
||||||
url?.startsWith('http://') || url?.startsWith('https://')
|
url?.startsWith('http://') || url?.startsWith('https://')
|
||||||
@@ -181,9 +202,10 @@ export default function Search({
|
|||||||
: `http://${url}`;
|
: `http://${url}`;
|
||||||
open(validUrl, '_self');
|
open(validUrl, '_self');
|
||||||
} else if (providerLabel.startsWith('Connect to')) {
|
} else if (providerLabel.startsWith('Connect to')) {
|
||||||
const client = guacamoleClients?.find(
|
const client = guacamoleClients?.find((client: GuacamoleClient) =>
|
||||||
(client: GuacamoleClient) =>
|
client.clientName
|
||||||
client.clientName.toLowerCase() === item.target.value.toLowerCase().substr(1),
|
.toLowerCase()
|
||||||
|
.startsWith(item.target.value.toLowerCase().substr(1)),
|
||||||
);
|
);
|
||||||
open(`${guacamoleHost}/#/client/${client?.clientIdentifier}`, '_self');
|
open(`${guacamoleHost}/#/client/${client?.clientIdentifier}`, '_self');
|
||||||
} else if (urlPattern.test(item.target.value)) {
|
} else if (urlPattern.test(item.target.value)) {
|
||||||
@@ -203,7 +225,6 @@ export default function Search({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyUp = (item: any) => {
|
const handleKeyUp = (item: any) => {
|
||||||
if (item.code === 'Escape') {
|
if (item.code === 'Escape') {
|
||||||
setListIndex(-1);
|
setListIndex(-1);
|
||||||
@@ -218,7 +239,7 @@ export default function Search({
|
|||||||
openDropdown();
|
openDropdown();
|
||||||
setPlaceholderValue(
|
setPlaceholderValue(
|
||||||
searchHistory.length > 0
|
searchHistory.length > 0
|
||||||
? `${searchHistory[listIndex].prefix} ${searchHistory[listIndex].searchTerm}`
|
? `${searchHistory[listIndex ?? 0]?.prefix} ${searchHistory[listIndex ?? 0]?.searchTerm}`
|
||||||
: '',
|
: '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -248,16 +269,21 @@ export default function Search({
|
|||||||
setValue(searchHistory[listIndex].id ?? 0);
|
setValue(searchHistory[listIndex].id ?? 0);
|
||||||
}
|
}
|
||||||
} else if (searchValue !== '') {
|
} else if (searchValue !== '') {
|
||||||
|
const filteredList: any = (searchValue as string).startsWith('!')
|
||||||
|
? filteredApplications
|
||||||
|
: (searchValue as string).startsWith('>')
|
||||||
|
? filteredGuacamoleClients
|
||||||
|
: filteredHistory;
|
||||||
if (item.code === 'ArrowUp') {
|
if (item.code === 'ArrowUp') {
|
||||||
if (listIndex === 0) {
|
if (listIndex === 0) {
|
||||||
setListIndex(filteredHistory.length - 1);
|
setListIndex(filteredList.length - 1);
|
||||||
} else if (listIndex >= filteredHistory.length) {
|
} else if (listIndex >= filteredList.length) {
|
||||||
setListIndex(filteredHistory.length - 1);
|
setListIndex(filteredList.length - 1);
|
||||||
} else {
|
} else {
|
||||||
setListIndex(listIndex - 1);
|
setListIndex(listIndex - 1);
|
||||||
}
|
}
|
||||||
} else if (item.code === 'ArrowDown') {
|
} else if (item.code === 'ArrowDown') {
|
||||||
if (listIndex >= filteredHistory.length - 1) {
|
if (listIndex >= filteredList.length - 1) {
|
||||||
setListIndex(0);
|
setListIndex(0);
|
||||||
} else if (value === 0) {
|
} else if (value === 0) {
|
||||||
setListIndex(0);
|
setListIndex(0);
|
||||||
@@ -266,10 +292,17 @@ export default function Search({
|
|||||||
setListIndex(listIndex + 1);
|
setListIndex(listIndex + 1);
|
||||||
}
|
}
|
||||||
} else if (item.code === 'ArrowRight' || item.code === 'Tab') {
|
} else if (item.code === 'ArrowRight' || item.code === 'Tab') {
|
||||||
|
if (Object.keys(filteredList[listIndex]).includes('provider')) {
|
||||||
setSearchValue(
|
setSearchValue(
|
||||||
`${filteredHistory[listIndex].prefix} ${filteredHistory[listIndex].searchTerm}`,
|
`${filteredList[listIndex].prefix} ${filteredList[listIndex].searchTerm}`,
|
||||||
);
|
);
|
||||||
setValue(filteredHistory[listIndex].id ?? 0);
|
} else if (Object.keys(filteredList[listIndex]).includes('clientName')) {
|
||||||
|
setSearchValue(`>${filteredList[listIndex].clientName}`);
|
||||||
|
} else if (Object.keys(filteredList[listIndex]).includes('name')) {
|
||||||
|
setSearchValue(`!${filteredList[listIndex].name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(filteredList[listIndex].id ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,26 +334,48 @@ export default function Search({
|
|||||||
} else if (item.target.value.startsWith('!')) {
|
} else if (item.target.value.startsWith('!')) {
|
||||||
if (item.target.value === '!w' || item.target.value === '!weather') {
|
if (item.target.value === '!w' || item.target.value === '!weather') {
|
||||||
setProviderLabel('Show weather');
|
setProviderLabel('Show weather');
|
||||||
|
setProviderIcon('FaCloudSun');
|
||||||
} else if (
|
} else if (
|
||||||
applications?.find(
|
applications?.find((app: Application) =>
|
||||||
(app: Application) =>
|
app.baseUrl.substring(1).toLowerCase().startsWith(item.target.value.substr(1)),
|
||||||
app.baseUrl.substring(1).toLowerCase() === item.target.value.substr(1),
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
setProviderLabel(`Launch ${item.target.value.substr(1)}`);
|
setProviderLabel(
|
||||||
|
`Launch ${
|
||||||
|
applications?.find((app: Application) =>
|
||||||
|
app.baseUrl
|
||||||
|
.substring(1)
|
||||||
|
.toLowerCase()
|
||||||
|
.startsWith(item.target.value.substr(1)),
|
||||||
|
)?.name
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
setProviderIcon('');
|
||||||
} else {
|
} else {
|
||||||
setProviderLabel('Invalid function');
|
setProviderLabel('Invalid function');
|
||||||
|
setProviderIcon('');
|
||||||
}
|
}
|
||||||
} else if (item.target.value.startsWith('>') && personal.enableGuacamole === 1) {
|
} else if (item.target.value.startsWith('>') && personal.enableGuacamole === 1) {
|
||||||
if (
|
if (
|
||||||
guacamoleClients?.find(
|
guacamoleClients?.find((client: GuacamoleClient) =>
|
||||||
(client: GuacamoleClient) =>
|
client.clientName
|
||||||
client.clientName.toLowerCase() === item.target.value.toLowerCase().substr(1),
|
.toLowerCase()
|
||||||
|
.startsWith(item.target.value.toLowerCase().substr(1)),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
setProviderLabel(`Connect to ${item.target.value.substr(1)}`);
|
setProviderLabel(
|
||||||
|
`Connect to ${
|
||||||
|
guacamoleClients?.find((client: GuacamoleClient) =>
|
||||||
|
client.clientName
|
||||||
|
.toLowerCase()
|
||||||
|
.startsWith(item.target.value.toLowerCase().substr(1)),
|
||||||
|
)?.clientName
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
setProviderIcon('');
|
||||||
} else {
|
} else {
|
||||||
setProviderLabel('Invalid connection');
|
setProviderLabel('Invalid connection');
|
||||||
|
setProviderIcon('');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setProviderLabel(defaultProviderLabel);
|
setProviderLabel(defaultProviderLabel);
|
||||||
@@ -333,11 +388,7 @@ export default function Search({
|
|||||||
const optionsFilter: OptionsFilter = ({ options, search }) => {
|
const optionsFilter: OptionsFilter = ({ options, search }) => {
|
||||||
const splittedSearch = search.toLowerCase().trim().split(' ');
|
const splittedSearch = search.toLowerCase().trim().split(' ');
|
||||||
if (splittedSearch[0].startsWith('!')) {
|
if (splittedSearch[0].startsWith('!')) {
|
||||||
return applicationData
|
return filteredApplications.map((app: Application) => {
|
||||||
.filter((app: Application) =>
|
|
||||||
app.name.toLowerCase().includes(splittedSearch[0].substring(1).toLowerCase()),
|
|
||||||
)
|
|
||||||
.map((app: Application) => {
|
|
||||||
return {
|
return {
|
||||||
label: app.name,
|
label: app.name,
|
||||||
value: app.baseUrl,
|
value: app.baseUrl,
|
||||||
@@ -346,13 +397,7 @@ export default function Search({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (splittedSearch[0].startsWith('>') && personal.enableGuacamole === 1) {
|
if (splittedSearch[0].startsWith('>') && personal.enableGuacamole === 1) {
|
||||||
return guacamoleClientData
|
return filteredGuacamoleClients.map((guacamoleClient: GuacamoleClient) => {
|
||||||
.filter((guacamoleClient: GuacamoleClient) =>
|
|
||||||
guacamoleClient.clientName
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(splittedSearch[0].substring(1).toLowerCase()),
|
|
||||||
)
|
|
||||||
.map((guacamoleClient: GuacamoleClient) => {
|
|
||||||
return {
|
return {
|
||||||
label: guacamoleClient.clientName,
|
label: guacamoleClient.clientName,
|
||||||
value: guacamoleClient.clientIdentifier,
|
value: guacamoleClient.clientIdentifier,
|
||||||
@@ -495,65 +540,8 @@ export default function Search({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (providerData && providerDataIsFetched) {
|
|
||||||
setProviders(providerData);
|
|
||||||
}
|
|
||||||
}, [providerData, providerDataIsFetched]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (guacamoleHostData && guacamoleHostDataIsFetched) {
|
|
||||||
setGuacamoleHost(guacamoleHostData[0].guacamoleHost);
|
|
||||||
}
|
|
||||||
}, [guacamoleClientData, guacamoleClientDataIsFetched]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (guacamoleClientData && guacamoleClientDataIsFetched) {
|
|
||||||
setGuacamoleClients(guacamoleClientData);
|
|
||||||
}
|
|
||||||
}, [guacamoleClientData, guacamoleClientDataIsFetched]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (guacamoleClients) {
|
|
||||||
setFilteredGuacamoleClients(
|
|
||||||
guacamoleClients.filter((client: GuacamoleClient) =>
|
|
||||||
client.clientName.toLowerCase().includes((searchValue as string).toLowerCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [guacamoleClients, searchValue]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (applicationData && !isApplicationDataFetching) {
|
|
||||||
setApplications(applicationData);
|
|
||||||
}
|
|
||||||
}, [applicationData, isApplicationDataFetching]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (applications) {
|
|
||||||
setFilteredApplications(
|
|
||||||
applications.filter((app: Application) =>
|
|
||||||
app.name.toLowerCase().includes((searchValue as string).toLowerCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [applications, searchValue]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (bookmarkData && bookmarksIsFetched) {
|
|
||||||
setBookmarks(
|
|
||||||
bookmarkData.filter(
|
|
||||||
(bookmark: Bookmark) =>
|
|
||||||
bookmark.enabled === 1 &&
|
|
||||||
bookmark.name.toLowerCase().includes((searchValue as string).toLowerCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [bookmarksIsFetched, bookmarkData, searchValue]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (historyData && historyDataIsFetched) {
|
if (historyData && historyDataIsFetched) {
|
||||||
setSearchHistory(historyData);
|
|
||||||
if (filteredHistory.length === 0) {
|
if (filteredHistory.length === 0) {
|
||||||
setFilteredHistory(historyData.slice(0, 10 * historyMultiplier));
|
setFilteredHistory(historyData.slice(0, 10 * historyMultiplier));
|
||||||
}
|
}
|
||||||
@@ -592,7 +580,6 @@ export default function Search({
|
|||||||
setDefaultProviderIcon(defaultProviderData[0]?.icon ?? 'FaGoogle');
|
setDefaultProviderIcon(defaultProviderData[0]?.icon ?? 'FaGoogle');
|
||||||
}
|
}
|
||||||
}, [defaultProviderData, defaultProviderDataIsFetched]);
|
}, [defaultProviderData, defaultProviderDataIsFetched]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{dropdownOpened && <Overlay blur={5} onClick={closeDropdown} fixed zIndex={200} />}
|
{dropdownOpened && <Overlay blur={5} onClick={closeDropdown} fixed zIndex={200} />}
|
||||||
@@ -654,7 +641,6 @@ export default function Search({
|
|||||||
}
|
}
|
||||||
rightSection={<div />}
|
rightSection={<div />}
|
||||||
clearable
|
clearable
|
||||||
onClear={handleResetSearch}
|
|
||||||
clearButtonProps={{
|
clearButtonProps={{
|
||||||
'aria-label': 'Clear search',
|
'aria-label': 'Clear search',
|
||||||
c: 'var(--mantine-color-accent-0)',
|
c: 'var(--mantine-color-accent-0)',
|
||||||
@@ -662,25 +648,26 @@ export default function Search({
|
|||||||
}}
|
}}
|
||||||
scrollAreaProps={{ viewportRef: ref }}
|
scrollAreaProps={{ viewportRef: ref }}
|
||||||
renderOption={(item: any) => renderItem(item)}
|
renderOption={(item: any) => renderItem(item)}
|
||||||
onBlur={() => {
|
|
||||||
setPlaceholderValue('');
|
|
||||||
setListIndex(-1);
|
|
||||||
}}
|
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
if (searchValue !== '') {
|
if (searchValue !== '') {
|
||||||
openDropdown();
|
openDropdown();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onKeyDown={(e) => {
|
onBlur={() => {
|
||||||
if (e.key === 'Tab') {
|
setPlaceholderValue('');
|
||||||
e.preventDefault();
|
setListIndex(-1);
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
|
onClear={handleResetSearch}
|
||||||
onDoubleClick={() => {
|
onDoubleClick={() => {
|
||||||
dropdownOpened ? closeDropdown() : openDropdown();
|
dropdownOpened ? closeDropdown() : openDropdown();
|
||||||
}}
|
}}
|
||||||
onInput={onInput}
|
onInput={onInput}
|
||||||
onKeyUp={handleKeyUp}
|
onKeyUp={handleKeyUp}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Tab') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{dropdownOpened && (
|
{dropdownOpened && (
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default function RecommendedBookmarks({
|
|||||||
backgroundColor: 'var(--mantine-color-search-0)',
|
backgroundColor: 'var(--mantine-color-search-0)',
|
||||||
backdropFilter: 'blur(15px) brightness(1.75)',
|
backdropFilter: 'blur(15px) brightness(1.75)',
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
opacity: searchValue && dropdownOpened && bookmarks.length > 0 ? 1 : 0,
|
opacity: searchValue && dropdownOpened ? 1 : 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack w={'100%'} mah={'636px'}>
|
<Stack w={'100%'} mah={'636px'}>
|
||||||
@@ -63,6 +63,22 @@ export default function RecommendedBookmarks({
|
|||||||
))
|
))
|
||||||
.toSorted((a: any, b: any) => (a.name > b.name ? 1 : -1))
|
.toSorted((a: any, b: any) => (a.name > b.name ? 1 : -1))
|
||||||
.slice(0, 5)}
|
.slice(0, 5)}
|
||||||
|
{!bookmarks.length && (
|
||||||
|
<Text
|
||||||
|
c={'var(--mantine-color-primary-0)'}
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--mantine-color-search-0)',
|
||||||
|
padding: '.5rem',
|
||||||
|
borderRadius: '10px',
|
||||||
|
overflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
No bookmarks found{' '}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack
|
<Stack
|
||||||
w={bookmarks.length <= 5 ? '0%' : '48%'}
|
w={bookmarks.length <= 5 ? '0%' : '48%'}
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ function SettingsButton() {
|
|||||||
const { data: appCategoryData, isFetched: appCategoryDataIsFetched } = useQueryAPI({
|
const { data: appCategoryData, isFetched: appCategoryDataIsFetched } = useQueryAPI({
|
||||||
endpoint: 'api/appCategories',
|
endpoint: 'api/appCategories',
|
||||||
});
|
});
|
||||||
const { data: wolClientData, isFetched: wolClientDataIsFetched } = useQueryAPI({
|
const { data: wolClientData } = useQueryAPI({
|
||||||
endpoint: 'api/wolClients',
|
endpoint: 'api/wolClients',
|
||||||
});
|
});
|
||||||
const { data: wolEnabledData, isFetched: wolEnabledDataIsFetched } = useQueryAPI({
|
const { data: wolEnabledData, isFetched: wolEnabledDataIsFetched } = useQueryAPI({
|
||||||
endpoint: 'api/wolEnabled',
|
endpoint: 'api/wolEnabled',
|
||||||
});
|
});
|
||||||
const [wolClients, setWolClients] = useState<WolClient[]>([]);
|
const wolClients = wolClientData || [];
|
||||||
const [enabled, setEnabled] = useState<number>(0);
|
const [enabled, setEnabled] = useState<number>(0);
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);
|
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);
|
||||||
@@ -64,12 +64,6 @@ function SettingsButton() {
|
|||||||
}
|
}
|
||||||
}, [appCategoryData, appCategoryDataIsFetched]);
|
}, [appCategoryData, appCategoryDataIsFetched]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (wolClientData && wolClientDataIsFetched) {
|
|
||||||
setWolClients(wolClientData);
|
|
||||||
}
|
|
||||||
}, [wolClientData, wolClientDataIsFetched]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wolEnabledData && wolEnabledDataIsFetched) {
|
if (wolEnabledData && wolEnabledDataIsFetched) {
|
||||||
setEnabled(wolEnabledData[0].enableWol);
|
setEnabled(wolEnabledData[0].enableWol);
|
||||||
|
|||||||
@@ -26,15 +26,11 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
|||||||
const { data: themePlaylistData } = useQueryAPI({
|
const { data: themePlaylistData } = useQueryAPI({
|
||||||
endpoint: 'api/themePlaylists',
|
endpoint: 'api/themePlaylists',
|
||||||
});
|
});
|
||||||
const { data: allThemeData, isFetched: allThemeDataIsFetched } = useQueryAPI({
|
const { data: allThemeData } = useQueryAPI({
|
||||||
endpoint: 'api/themes',
|
endpoint: 'api/themes',
|
||||||
});
|
});
|
||||||
const [allThemes, setAllThemes] = useState<Theme[]>([]);
|
const allThemes: Theme[] = allThemeData || [];
|
||||||
useEffect(() => {
|
|
||||||
if (allThemeData && allThemeDataIsFetched) {
|
|
||||||
setAllThemes(allThemeData);
|
|
||||||
}
|
|
||||||
}, [allThemeData, allThemeDataIsFetched]);
|
|
||||||
const {
|
const {
|
||||||
isMobile,
|
isMobile,
|
||||||
currentTheme,
|
currentTheme,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ActionIcon, Divider, Group, ScrollArea, Stack, Tabs, Text } from '@mantine/core';
|
import { ActionIcon, Divider, Group, ScrollArea, Stack, Tabs, Text } from '@mantine/core';
|
||||||
import { useCommonHooks, useQueryAPI } from 'hooks';
|
import { useCommonHooks, useQueryAPI } from 'hooks';
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { FaPaintBrush, FaRegListAlt } from 'react-icons/fa';
|
import { FaPaintBrush, FaRegListAlt } from 'react-icons/fa';
|
||||||
import {
|
import {
|
||||||
FiArrowLeftCircle,
|
FiArrowLeftCircle,
|
||||||
@@ -27,19 +26,14 @@ import WolClients from './components/WoL/WolClients';
|
|||||||
|
|
||||||
export function Settings() {
|
export function Settings() {
|
||||||
const endpoint = 'api/personalData';
|
const endpoint = 'api/personalData';
|
||||||
const { data: personalData, isFetched: personalDataIsFetched } = useQueryAPI({
|
const { data: personalData } = useQueryAPI({
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const [personal, setPersonal] = useState<PersonalData>();
|
const personal: PersonalData = personalData?.[0];
|
||||||
const { isMobile, theme } = useCommonHooks();
|
const { isMobile, theme } = useCommonHooks();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { tabValue } = useParams();
|
const { tabValue } = useParams();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (personalData && personalDataIsFetched) {
|
|
||||||
setPersonal(personalData[0]);
|
|
||||||
}
|
|
||||||
}, [personalData, personalDataIsFetched]);
|
|
||||||
return (
|
return (
|
||||||
<Stack h={'100%'} gap={0}>
|
<Stack h={'100%'} gap={0}>
|
||||||
<Group gap={0} style={{ position: 'relative', paddingBottom: '1rem' }}>
|
<Group gap={0} style={{ position: 'relative', paddingBottom: '1rem' }}>
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ const ApplicationSettings = () => {
|
|||||||
index: 0,
|
index: 0,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
};
|
};
|
||||||
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: defaultApplication,
|
initialValues: defaultApplication,
|
||||||
validate: {
|
validate: {
|
||||||
@@ -49,19 +51,21 @@ const ApplicationSettings = () => {
|
|||||||
});
|
});
|
||||||
const appsEndpoint = 'api/applications';
|
const appsEndpoint = 'api/applications';
|
||||||
const appsOrderEndpoint = 'api/applicationOrder';
|
const appsOrderEndpoint = 'api/applicationOrder';
|
||||||
const {
|
const { data: applicationData, isFetching: applicationDataIsFetching } = useQueryAPI({
|
||||||
data: applicationData,
|
|
||||||
isFetched: applicationDataIsFetched,
|
|
||||||
isFetching: applicationDataIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: appsEndpoint,
|
endpoint: appsEndpoint,
|
||||||
});
|
});
|
||||||
const [applications, setApplications] = useState<Application[]>([]);
|
const applications: Application[] =
|
||||||
|
applicationData
|
||||||
|
.filter(
|
||||||
|
(a: Application) =>
|
||||||
|
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
|
a.url.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
|
a.baseUrl.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
||||||
|
)
|
||||||
|
.sort((a: Application, b: Application) => (a.index > b.index ? 1 : -1)) || [];
|
||||||
const [applicationList, setApplicationList] = useState<Application[]>([]);
|
const [applicationList, setApplicationList] = useState<Application[]>([]);
|
||||||
const { isMobile, reorder, theme } = useCommonHooks();
|
const { isMobile, reorder, theme } = useCommonHooks();
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
|
||||||
const [dragging, setDragging] = useState(false);
|
const [dragging, setDragging] = useState(false);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -93,22 +97,10 @@ const ApplicationSettings = () => {
|
|||||||
form.reset();
|
form.reset();
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (applicationData && applicationDataIsFetched) {
|
|
||||||
setApplications(
|
|
||||||
applicationData
|
|
||||||
.filter(
|
|
||||||
(a: Application) =>
|
|
||||||
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
|
||||||
a.url.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
|
||||||
a.baseUrl.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
|
||||||
)
|
|
||||||
.sort((a: Application, b: Application) => (a.index > b.index ? 1 : -1)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [applicationData, applicationDataIsFetched, debouncedSearch]);
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (applicationList.length > 0 && applicationList !== applications) {
|
if (applicationList.length > 0 && applicationList !== applications) {
|
||||||
|
|||||||
@@ -33,14 +33,10 @@ const BookmarkSettings = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const bookmarksEndpoint = 'api/bookmarks';
|
const bookmarksEndpoint = 'api/bookmarks';
|
||||||
const {
|
const { data: bookmarkData, isFetching: bookmarkDataIsFetching } = useQueryAPI({
|
||||||
data: bookmarkData,
|
|
||||||
isFetched: bookmarkDataIsFetched,
|
|
||||||
isFetching: bookmarkDataIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: bookmarksEndpoint,
|
endpoint: bookmarksEndpoint,
|
||||||
});
|
});
|
||||||
const [bookmarks, setBookmarks] = useState<Bookmark[]>([]);
|
const bookmarks = bookmarkData || [];
|
||||||
const categoriesEndpoint = 'api/bookmarkCategories';
|
const categoriesEndpoint = 'api/bookmarkCategories';
|
||||||
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
||||||
endpoint: categoriesEndpoint,
|
endpoint: categoriesEndpoint,
|
||||||
@@ -54,7 +50,27 @@ const BookmarkSettings = () => {
|
|||||||
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 [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const foundBookmarkGroups = [
|
||||||
|
...new Set(
|
||||||
|
bookmarks
|
||||||
|
.filter(
|
||||||
|
(a: Bookmark) =>
|
||||||
|
a.name
|
||||||
|
.toLowerCase()
|
||||||
|
.includes((debouncedGroupSearch as string).toLowerCase()) ||
|
||||||
|
a.url.toLowerCase().includes((debouncedGroupSearch as string).toLowerCase()),
|
||||||
|
)
|
||||||
|
.map((a: Bookmark) =>
|
||||||
|
groupData.find(
|
||||||
|
(b: BookmarkGroup) =>
|
||||||
|
b.categoryName.toLowerCase() === a.category.toLowerCase(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const foundGroups = groupData.filter((a: BookmarkGroup) =>
|
||||||
|
a.categoryName.toLowerCase().includes((debouncedGroupSearch as string).toLowerCase()),
|
||||||
|
);
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
form.reset();
|
form.reset();
|
||||||
@@ -80,40 +96,8 @@ const BookmarkSettings = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (bookmarkData && bookmarkDataIsFetched) {
|
|
||||||
setBookmarks(bookmarkData);
|
|
||||||
}
|
|
||||||
}, [bookmarkDataIsFetched, bookmarkData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (groupData && groupsIsFetched) {
|
if (groupData && groupsIsFetched) {
|
||||||
const foundBookmarkGroups = [
|
|
||||||
...new Set(
|
|
||||||
bookmarks
|
|
||||||
.filter(
|
|
||||||
(a: Bookmark) =>
|
|
||||||
a.name
|
|
||||||
.toLowerCase()
|
|
||||||
.includes((debouncedGroupSearch as string).toLowerCase()) ||
|
|
||||||
a.url
|
|
||||||
.toLowerCase()
|
|
||||||
.includes((debouncedGroupSearch as string).toLowerCase()),
|
|
||||||
)
|
|
||||||
.map((a: Bookmark) =>
|
|
||||||
groupData.find(
|
|
||||||
(b: BookmarkGroup) =>
|
|
||||||
b.categoryName.toLowerCase() === a.category.toLowerCase(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
const foundGroups = groupData.filter((a: BookmarkGroup) =>
|
|
||||||
a.categoryName
|
|
||||||
.toLowerCase()
|
|
||||||
.includes((debouncedGroupSearch as string).toLowerCase()),
|
|
||||||
);
|
|
||||||
|
|
||||||
const groupList = [...new Set([...foundBookmarkGroups, ...foundGroups])].sort(
|
const groupList = [...new Set([...foundBookmarkGroups, ...foundGroups])].sort(
|
||||||
(a: BookmarkGroup, b: BookmarkGroup) =>
|
(a: BookmarkGroup, b: BookmarkGroup) =>
|
||||||
a.categoryIndex > b.categoryIndex ? 1 : -1,
|
a.categoryIndex > b.categoryIndex ? 1 : -1,
|
||||||
|
|||||||
@@ -16,11 +16,15 @@ function DebouncedBookmarkItem({
|
|||||||
const { handleDelete, isMobile, theme } = useCommonHooks();
|
const { handleDelete, isMobile, theme } = useCommonHooks();
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
const { data: groupData } = useQueryAPI({
|
||||||
endpoint: 'api/bookmarkCategories',
|
endpoint: 'api/bookmarkCategories',
|
||||||
});
|
});
|
||||||
const [bookmark, setBookmark] = useState<Bookmark>(bookmarkItem);
|
const [bookmark, setBookmark] = useState<Bookmark>(bookmarkItem);
|
||||||
const [groups, setGroups] = useState<BookmarkGroup[]>([]);
|
const groups: BookmarkGroup[] =
|
||||||
|
groupData.sort(
|
||||||
|
(a: BookmarkGroup, b: BookmarkGroup) =>
|
||||||
|
a.categoryName.toLocaleLowerCase() > b.categoryName.toLocaleLowerCase(),
|
||||||
|
) || [];
|
||||||
const [update, setUpdate] = useState<any>({});
|
const [update, setUpdate] = useState<any>({});
|
||||||
const [debounced] = useDebouncedValue(update, 1000);
|
const [debounced] = useDebouncedValue(update, 1000);
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
@@ -39,17 +43,6 @@ function DebouncedBookmarkItem({
|
|||||||
}
|
}
|
||||||
}, [update]);
|
}, [update]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (groupData) {
|
|
||||||
setGroups(
|
|
||||||
groupData.sort(
|
|
||||||
(a: BookmarkGroup, b: BookmarkGroup) =>
|
|
||||||
a.categoryName.toLocaleLowerCase() > b.categoryName.toLocaleLowerCase(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [groupData, groupsIsFetched]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
p={isMobile ? 'xl' : undefined}
|
p={isMobile ? 'xl' : undefined}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { isNotEmpty, useForm } from '@mantine/form';
|
|||||||
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
||||||
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
|
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
|
||||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { FaTerminal } from 'react-icons/fa';
|
import { FaTerminal } from 'react-icons/fa';
|
||||||
import { FaComputer, FaDesktop } from 'react-icons/fa6';
|
import { FaComputer, FaDesktop } from 'react-icons/fa6';
|
||||||
import { popoverClasses, selectClasses, textInputClasses } from 'styles';
|
import { popoverClasses, selectClasses, textInputClasses } from 'styles';
|
||||||
@@ -39,20 +39,24 @@ const GuacamoleClients = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const types: string[] = ['SSH', 'RDP', 'VNC'];
|
const types: string[] = ['SSH', 'RDP', 'VNC'];
|
||||||
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
const endpoint = 'api/guacamoleClients';
|
const endpoint = 'api/guacamoleClients';
|
||||||
const {
|
const { data: guacamoleClientsData, isFetching: guacamoleClientsIsFetching } =
|
||||||
data: guacamoleClientsData,
|
useQueryAPI({
|
||||||
isFetched: guacamoleClientsIsFetched,
|
|
||||||
isFetching: guacamoleClientsIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const [guacamoleClients, setGuacamoleClients] = useState<GuacamoleClient[]>([]);
|
const guacamoleClients =
|
||||||
|
guacamoleClientsData.filter(
|
||||||
|
(a: GuacamoleClient) =>
|
||||||
|
a.clientName.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
|
a.clientIdentifier
|
||||||
|
.toLowerCase()
|
||||||
|
.includes((debouncedSearch as string).toLowerCase()),
|
||||||
|
) || [];
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const { isMobile, theme } = useCommonHooks();
|
const { isMobile, theme } = useCommonHooks();
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@@ -61,25 +65,6 @@ const GuacamoleClients = () => {
|
|||||||
form.reset();
|
form.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (guacamoleClientsData && guacamoleClientsIsFetched) {
|
|
||||||
setGuacamoleClients(
|
|
||||||
guacamoleClientsData.filter(
|
|
||||||
(a: GuacamoleClient) =>
|
|
||||||
a.clientName
|
|
||||||
.toLowerCase()
|
|
||||||
.includes((debouncedSearch as string).toLowerCase()) ||
|
|
||||||
a.clientIdentifier
|
|
||||||
.toLowerCase()
|
|
||||||
.includes((debouncedSearch as string).toLowerCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (debouncedSearch === searchValue) {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
}, [guacamoleClientsData, guacamoleClientsIsFetched, debouncedSearch]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<AddItemModal
|
<AddItemModal
|
||||||
|
|||||||
@@ -18,11 +18,7 @@ import DebouncedMessage from './DebouncedMessage';
|
|||||||
|
|
||||||
const MessageSettings = () => {
|
const MessageSettings = () => {
|
||||||
const endpoint = 'api/messages';
|
const endpoint = 'api/messages';
|
||||||
const {
|
const { data: messageData, isFetching: messageDataIsFetching } = useQueryAPI({
|
||||||
data: messageData,
|
|
||||||
isFetched: messageDataIsFetched,
|
|
||||||
isFetching: messageDataIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
@@ -33,13 +29,16 @@ const MessageSettings = () => {
|
|||||||
message: isNotEmpty('Message is required'),
|
message: isNotEmpty('Message is required'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { isMobile, theme } = useCommonHooks();
|
|
||||||
const { mutateAsync } = useMutationAPI();
|
|
||||||
const [messages, setMessages] = useState<Message[]>([]);
|
|
||||||
const [message, setMessage] = useState({});
|
|
||||||
const [debounced] = useDebouncedValue(message, 1000);
|
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
|
const { isMobile, theme } = useCommonHooks();
|
||||||
|
const { mutateAsync } = useMutationAPI();
|
||||||
|
const messages =
|
||||||
|
messageData.filter((m: Message) =>
|
||||||
|
m.message.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
||||||
|
) || [];
|
||||||
|
const [message, setMessage] = useState({});
|
||||||
|
const [debounced] = useDebouncedValue(message, 1000);
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@@ -48,19 +47,6 @@ const MessageSettings = () => {
|
|||||||
form.reset();
|
form.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (messageData && messageDataIsFetched) {
|
|
||||||
setMessages(
|
|
||||||
messageData.filter((m: Message) =>
|
|
||||||
m.message.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (debouncedSearch === searchValue) {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
}, [messageData, messageDataIsFetched, debouncedSearch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(debounced).length) {
|
if (Object.keys(debounced).length) {
|
||||||
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
|
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
|
||||||
|
|||||||
@@ -9,14 +9,10 @@ import type { SearchHistoryItem } from 'types';
|
|||||||
|
|
||||||
const SearchHistory = () => {
|
const SearchHistory = () => {
|
||||||
const searchEndpoint = 'api/searchHistory';
|
const searchEndpoint = 'api/searchHistory';
|
||||||
const {
|
const { data: historyData, isFetching: historyDataIsFetching } = useQueryAPI({
|
||||||
data: historyData,
|
|
||||||
isFetched: historyDataIsFetched,
|
|
||||||
isFetching: historyDataIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: searchEndpoint,
|
endpoint: searchEndpoint,
|
||||||
});
|
});
|
||||||
const [searchHistory, setSearchHistory] = useState<SearchHistoryItem[]>([]);
|
const searchHistory: SearchHistoryItem[] = historyData || [];
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
const [searchHistoryItem, setSearchHistoryItem] = useState<SearchHistoryItem>();
|
const [searchHistoryItem, setSearchHistoryItem] = useState<SearchHistoryItem>();
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
@@ -36,12 +32,6 @@ const SearchHistory = () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (historyData && historyDataIsFetched) {
|
|
||||||
setSearchHistory(historyData);
|
|
||||||
}
|
|
||||||
}, [historyData, historyDataIsFetched]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setGlobalFilter(debouncedSearch);
|
setGlobalFilter(debouncedSearch);
|
||||||
if (debouncedSearch === globalFilter) {
|
if (debouncedSearch === globalFilter) {
|
||||||
|
|||||||
@@ -52,19 +52,23 @@ const SearchProviderSettings = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const endpoint = 'api/providers';
|
const endpoint = 'api/providers';
|
||||||
const {
|
const { data: providerData, isFetching: providerDataIsFetching } = useQueryAPI({
|
||||||
data: providerData,
|
|
||||||
isFetched: providerDataIsFetched,
|
|
||||||
isFetching: providerDataIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const [providers, setProviders] = useState<Provider[]>([]);
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
|
const providers =
|
||||||
|
providerData
|
||||||
|
.filter(
|
||||||
|
(a: Provider) =>
|
||||||
|
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
|
a.url.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
|
a.prefix.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
||||||
|
)
|
||||||
|
.sort((a: Provider, b: Provider) => (a.prefix > b.prefix ? 1 : -1)) ?? [];
|
||||||
const { isMobile, theme, iconFilter } = useCommonHooks();
|
const { isMobile, theme, iconFilter } = useCommonHooks();
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@@ -74,22 +78,10 @@ const SearchProviderSettings = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (providerData && providerDataIsFetched) {
|
|
||||||
setProviders(
|
|
||||||
providerData
|
|
||||||
.filter(
|
|
||||||
(a: Provider) =>
|
|
||||||
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
|
||||||
a.url.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
|
||||||
a.prefix.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
|
||||||
)
|
|
||||||
.sort((a: Provider, b: Provider) => (a.prefix > b.prefix ? 1 : -1)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [providerData, providerDataIsFetched, debouncedSearch]);
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
|
|||||||
@@ -15,14 +15,18 @@ export function ThemePlaylists() {
|
|||||||
endpoint: themesEndpoint,
|
endpoint: themesEndpoint,
|
||||||
});
|
});
|
||||||
const themePlaylistsEndpoint = 'api/themePlaylists';
|
const themePlaylistsEndpoint = 'api/themePlaylists';
|
||||||
const {
|
const { data: themePlaylistData, isFetching: themePlaylistDataIsFetching } =
|
||||||
data: themePlaylistData,
|
useQueryAPI({
|
||||||
isFetched: themePlaylistDataIsFetched,
|
|
||||||
isFetching: themePlaylistDataIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: themePlaylistsEndpoint,
|
endpoint: themePlaylistsEndpoint,
|
||||||
});
|
});
|
||||||
const [themePlaylists, setThemePlaylists] = useState<ThemePlaylist[]>([]);
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
|
const themePlaylists: ThemePlaylist[] =
|
||||||
|
themePlaylistData?.filter(
|
||||||
|
(a: ThemePlaylist) =>
|
||||||
|
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
|
a.name.includes('Time of Day'),
|
||||||
|
) || [];
|
||||||
const defaultPlaylist = {
|
const defaultPlaylist = {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
default: 0,
|
default: 0,
|
||||||
@@ -34,8 +38,6 @@ export function ThemePlaylists() {
|
|||||||
const [selectedPlaylist, setSelectedPlaylist] =
|
const [selectedPlaylist, setSelectedPlaylist] =
|
||||||
useState<ThemePlaylist>(defaultPlaylist);
|
useState<ThemePlaylist>(defaultPlaylist);
|
||||||
const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
|
const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const handleOpen = (playlist: ThemePlaylist) => {
|
const handleOpen = (playlist: ThemePlaylist) => {
|
||||||
@@ -58,19 +60,10 @@ export function ThemePlaylists() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (themePlaylistData && themePlaylistDataIsFetched) {
|
|
||||||
setThemePlaylists(
|
|
||||||
themePlaylistData.filter(
|
|
||||||
(a: ThemePlaylist) =>
|
|
||||||
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
|
||||||
a.name.includes('Time of Day'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [themePlaylistData, themePlaylistDataIsFetched, debouncedSearch]);
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ import { ThemeModal } from './ThemeModal';
|
|||||||
|
|
||||||
export function Themes() {
|
export function Themes() {
|
||||||
const backgroundsEndpoint = 'api/backgrounds';
|
const backgroundsEndpoint = 'api/backgrounds';
|
||||||
const {
|
const { data: allThemeData, isFetching: allThemeDataIsFetching } = useQueryAPI({
|
||||||
data: allThemeData,
|
|
||||||
isFetched: allThemeDataIsFetched,
|
|
||||||
isFetching: allThemeDataIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: 'api/themes',
|
endpoint: 'api/themes',
|
||||||
});
|
});
|
||||||
const [allThemes, setAllThemes] = useState<Theme[]>([]);
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
|
const allThemes: Theme[] = allThemeData?.filter((a: Theme) =>
|
||||||
|
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
||||||
|
);
|
||||||
const {
|
const {
|
||||||
data: backgroundData,
|
data: backgroundData,
|
||||||
isFetched: backgroundDataIsFetched,
|
isFetched: backgroundDataIsFetched,
|
||||||
@@ -43,7 +43,7 @@ export function Themes() {
|
|||||||
const { data: themePlaylistData } = useQueryAPI({
|
const { data: themePlaylistData } = useQueryAPI({
|
||||||
endpoint: 'api/themePlaylists',
|
endpoint: 'api/themePlaylists',
|
||||||
});
|
});
|
||||||
const [themePlaylists, setThemePlaylists] = useState<ThemePlaylist[]>([]);
|
const themePlaylists: ThemePlaylist[] = themePlaylistData || [];
|
||||||
const [themeModalOpened, { open: openThemeModal, close: closeThemeModal }] =
|
const [themeModalOpened, { open: openThemeModal, close: closeThemeModal }] =
|
||||||
useDisclosure(false);
|
useDisclosure(false);
|
||||||
const [
|
const [
|
||||||
@@ -51,7 +51,18 @@ export function Themes() {
|
|||||||
{ open: openBackgroundModal, close: closeBackgroundModal },
|
{ open: openBackgroundModal, close: closeBackgroundModal },
|
||||||
] = useDisclosure(false);
|
] = useDisclosure(false);
|
||||||
const [backgroundList, setBackgroundList] = useState<string[]>(['None']);
|
const [backgroundList, setBackgroundList] = useState<string[]>(['None']);
|
||||||
const [inUseBackgrounds, setInUseBackgrounds] = useState<string[]>([]);
|
const inUseBackgrounds: string[] = [
|
||||||
|
...new Set(
|
||||||
|
allThemes
|
||||||
|
?.filter((t) => {
|
||||||
|
if (t.backgroundImage !== 'none') {
|
||||||
|
return t.backgroundImage;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
.map((t) => t.backgroundImage),
|
||||||
|
),
|
||||||
|
] as string[];
|
||||||
const [selectedTheme, setSelectedTheme] = useState<Theme>({
|
const [selectedTheme, setSelectedTheme] = useState<Theme>({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
name: 'default',
|
name: 'default',
|
||||||
@@ -65,8 +76,6 @@ export function Themes() {
|
|||||||
textShadowsEnabled: 0,
|
textShadowsEnabled: 0,
|
||||||
appShadowsEnabled: 0,
|
appShadowsEnabled: 0,
|
||||||
});
|
});
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isNew, setIsNew] = useState(false);
|
const [isNew, setIsNew] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -94,40 +103,10 @@ export function Themes() {
|
|||||||
}, [backgroundData, backgroundDataIsFetched]);
|
}, [backgroundData, backgroundDataIsFetched]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (themePlaylistData) {
|
|
||||||
setThemePlaylists(themePlaylistData);
|
|
||||||
}
|
|
||||||
}, [themePlaylistData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (allThemeData && allThemeDataIsFetched) {
|
|
||||||
setAllThemes(
|
|
||||||
allThemeData.filter((a: Theme) =>
|
|
||||||
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [allThemeData, allThemeDataIsFetched, debouncedSearch]);
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (allThemes) {
|
|
||||||
setInUseBackgrounds([
|
|
||||||
...new Set(
|
|
||||||
allThemes
|
|
||||||
.filter((t) => {
|
|
||||||
if (t.backgroundImage !== 'none') {
|
|
||||||
return t.backgroundImage;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
})
|
|
||||||
.map((t) => t.backgroundImage),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}, [allThemes]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
@@ -195,7 +174,7 @@ export function Themes() {
|
|||||||
}}
|
}}
|
||||||
pos={'absolute'}
|
pos={'absolute'}
|
||||||
/>
|
/>
|
||||||
{allThemes.length > 0 ? (
|
{allThemes?.length > 0 ? (
|
||||||
allThemes.map((cardTheme: Theme) => (
|
allThemes.map((cardTheme: Theme) => (
|
||||||
<Group key={cardTheme.name} pos={'relative'}>
|
<Group key={cardTheme.name} pos={'relative'}>
|
||||||
{cardTheme.name === currentTheme && (
|
{cardTheme.name === currentTheme && (
|
||||||
|
|||||||
@@ -35,19 +35,20 @@ const WolClientSettings = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const endpoint = 'api/wolClients';
|
const endpoint = 'api/wolClients';
|
||||||
const {
|
const { data: wolClientsData, isFetching: wolClientsIsFetching } = useQueryAPI({
|
||||||
data: wolClientsData,
|
|
||||||
isFetched: wolClientsIsFetched,
|
|
||||||
isFetching: wolClientsIsFetching,
|
|
||||||
} = useQueryAPI({
|
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const [wolClients, setWolClients] = useState<WolClient[]>([]);
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
|
const wolClients: WolClient[] =
|
||||||
|
wolClientsData.filter(
|
||||||
|
(a: WolClient) =>
|
||||||
|
a.hostname.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
|
a.macAddress.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
|
||||||
|
) || [];
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const { isMobile, theme } = useCommonHooks();
|
const { isMobile, theme } = useCommonHooks();
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@@ -57,23 +58,10 @@ const WolClientSettings = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wolClientsData && wolClientsIsFetched) {
|
|
||||||
setWolClients(
|
|
||||||
wolClientsData.filter(
|
|
||||||
(a: WolClient) =>
|
|
||||||
a.hostname
|
|
||||||
.toLowerCase()
|
|
||||||
.includes((debouncedSearch as string).toLowerCase()) ||
|
|
||||||
a.macAddress
|
|
||||||
.toLowerCase()
|
|
||||||
.includes((debouncedSearch as string).toLowerCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [wolClientsData, wolClientsIsFetched, debouncedSearch]);
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
|
|||||||
Reference in New Issue
Block a user