Standardization
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.10.0",
|
"@types/node": "^24.10.0",
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.3",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"@vitejs/plugin-react": "^5.1.0",
|
"@vitejs/plugin-react": "^5.1.0",
|
||||||
"@vitest/coverage-v8": "^4.0.8",
|
"@vitest/coverage-v8": "^4.0.8",
|
||||||
@@ -1868,9 +1868,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.2.2",
|
"version": "19.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.3.tgz",
|
||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-k5dJVszUiNr1DSe8Cs+knKR6IrqhqdhpUwzqhkS8ecQTSf3THNtbfIp/umqHMpX2bv+9dkx3fwDv/86LcSfvSg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.10.0",
|
"@types/node": "^24.10.0",
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.3",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"@vitejs/plugin-react": "^5.1.0",
|
"@vitejs/plugin-react": "^5.1.0",
|
||||||
"@vitest/coverage-v8": "^4.0.8",
|
"@vitest/coverage-v8": "^4.0.8",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function AppItem({
|
|||||||
const { ref, width, height } = useElementSize();
|
const { ref, width, height } = useElementSize();
|
||||||
const allIcons: any = { ...fa6, ...vsc, ...si };
|
const allIcons: any = { ...fa6, ...vsc, ...si };
|
||||||
const Icon = allIcons[icon];
|
const Icon = allIcons[icon];
|
||||||
const { isMobile, filter } = useCommonHooks();
|
const { isMobile, iconFilter } = useCommonHooks();
|
||||||
const [textDecoration, setTextDecoration] = useState<string>('none');
|
const [textDecoration, setTextDecoration] = useState<string>('none');
|
||||||
const [hover, setHover] = useState<boolean>(false);
|
const [hover, setHover] = useState<boolean>(false);
|
||||||
const validUrl =
|
const validUrl =
|
||||||
@@ -102,7 +102,7 @@ export function AppItem({
|
|||||||
style={{
|
style={{
|
||||||
height: scale * 32,
|
height: scale * 32,
|
||||||
width: scale * 32,
|
width: scale * 32,
|
||||||
filter: filter(selectedTheme),
|
filter: iconFilter(selectedTheme),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export function DataTable({
|
|||||||
filtersEnabled,
|
filtersEnabled,
|
||||||
globalFilter,
|
globalFilter,
|
||||||
setGlobalFilter,
|
setGlobalFilter,
|
||||||
|
isRefreshing,
|
||||||
}: {
|
}: {
|
||||||
data: any[];
|
data: any[];
|
||||||
columns: any[];
|
columns: any[];
|
||||||
@@ -30,6 +31,7 @@ export function DataTable({
|
|||||||
filtersEnabled: boolean;
|
filtersEnabled: boolean;
|
||||||
globalFilter: string;
|
globalFilter: string;
|
||||||
setGlobalFilter: Dispatch<SetStateAction<string>>;
|
setGlobalFilter: Dispatch<SetStateAction<string>>;
|
||||||
|
isRefreshing?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
@@ -192,7 +194,11 @@ export function DataTable({
|
|||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot
|
||||||
|
style={{
|
||||||
|
transition: 'all 1s ease',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{table.getFooterGroups().map((footerGroup) => (
|
{table.getFooterGroups().map((footerGroup) => (
|
||||||
<tr key={footerGroup.id}>
|
<tr key={footerGroup.id}>
|
||||||
{footerGroup.headers.map((header) => (
|
{footerGroup.headers.map((header) => (
|
||||||
@@ -223,6 +229,29 @@ export function DataTable({
|
|||||||
</Text>
|
</Text>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: 'var(--mantine-color-accent-0)',
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: isRefreshing ? 0 : -30,
|
||||||
|
transition: 'all 1.5s ease-in-out',
|
||||||
|
placeContent: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<td colSpan={5} width={'100%'} style={{}}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: 'var(--mantine-color-background-0)',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Updating
|
||||||
|
</Text>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useMutationAPI } from './useAPI';
|
|||||||
|
|
||||||
export function useCommonHooks() {
|
export function useCommonHooks() {
|
||||||
const { mutateAsync } = useMutationAPI();
|
const { mutateAsync } = useMutationAPI();
|
||||||
const isMobile = window.matchMedia('(max-width: 600px)').matches;
|
const isMobile = globalThis.matchMedia('(max-width: 600px)').matches;
|
||||||
const [theme, setTheme] = useAtom<Theme>(appTheme);
|
const [theme, setTheme] = useAtom<Theme>(appTheme);
|
||||||
const [themeName, setThemeName] = useLocalStorage<string>({
|
const [themeName, setThemeName] = useLocalStorage<string>({
|
||||||
key: 'theme',
|
key: 'theme',
|
||||||
@@ -109,12 +109,12 @@ export function useCommonHooks() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const urlPattern = new RegExp(
|
const urlPattern = new RegExp(
|
||||||
'^(https?:\\/\\/)?' + // protocol
|
String.raw`^(https?:\/\/)?` +
|
||||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
String.raw`((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|` +
|
||||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
|
String.raw`((\d{1,3}\.){3}\d{1,3}))` +
|
||||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
|
String.raw`(\:\d+)?(\/[-a-z\d%_.~+]*)*` +
|
||||||
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
|
String.raw`(\?[;&a-z\d%_.~+=-]*)?` +
|
||||||
'(\\#[-a-z\\d_]*)?$',
|
String.raw`(\#[-a-z\d_]*)?$`,
|
||||||
'i',
|
'i',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -152,8 +152,8 @@ export function useCommonHooks() {
|
|||||||
mutateAsync({ method: 'DELETE', endpoint: endpoint, body: item });
|
mutateAsync({ method: 'DELETE', endpoint: endpoint, body: item });
|
||||||
};
|
};
|
||||||
|
|
||||||
const filter = (selectedTheme?: Theme) => {
|
const iconFilter = (selectedTheme?: Theme) => {
|
||||||
if (selectedTheme && selectedTheme.appShadowsEnabled === 1) {
|
if (selectedTheme?.appShadowsEnabled === 1) {
|
||||||
return 'drop-shadow(2px 2px color-mix(in srgb, var(--mantine-color-accent-0), transparent 30%))';
|
return 'drop-shadow(2px 2px color-mix(in srgb, var(--mantine-color-accent-0), transparent 30%))';
|
||||||
}
|
}
|
||||||
if (!selectedTheme && theme.appShadowsEnabled === 1) {
|
if (!selectedTheme && theme.appShadowsEnabled === 1) {
|
||||||
@@ -163,14 +163,14 @@ export function useCommonHooks() {
|
|||||||
return 'none';
|
return 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
function fixDigits(k: number) {
|
const fixDigits = (k: number) => {
|
||||||
if (k < 10) {
|
if (k < 10) {
|
||||||
return `0${k}`;
|
return `0${k}`;
|
||||||
}
|
}
|
||||||
return k;
|
return k;
|
||||||
}
|
};
|
||||||
|
|
||||||
function getObjectDiff(original: any, current: any) {
|
const getObjectDiff = (original: any, current: any) => {
|
||||||
const changes: any = {};
|
const changes: any = {};
|
||||||
|
|
||||||
// Check current object's properties
|
// Check current object's properties
|
||||||
@@ -210,14 +210,14 @@ export function useCommonHooks() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(changes).length === 0 ? null : changes;
|
return Object.keys(changes).length === 0 ? null : changes;
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isMobile,
|
isMobile,
|
||||||
theme,
|
theme,
|
||||||
setTheme,
|
setTheme,
|
||||||
urlPattern,
|
urlPattern,
|
||||||
filter,
|
iconFilter,
|
||||||
fixDigits,
|
fixDigits,
|
||||||
reorder,
|
reorder,
|
||||||
getObjectDiff,
|
getObjectDiff,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ function SettingsCard({
|
|||||||
{sectionTitle}
|
{sectionTitle}
|
||||||
</Text>
|
</Text>
|
||||||
<Group w={'100%'} justify='center'>
|
<Group w={'100%'} justify='center'>
|
||||||
<Stack w={scale !== undefined ? '60%' : '90%'} align='center'>
|
<Stack w={scale === undefined ? '90%' : '60%'} align='center'>
|
||||||
<Text fz={'.875rem'} mt={'1rem'}>
|
<Text fz={'.875rem'} mt={'1rem'}>
|
||||||
Density
|
Density
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ function ThemingCard() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (theme.name !== current) {
|
if (theme.name === current) {
|
||||||
start();
|
|
||||||
} else {
|
|
||||||
setCurrent(theme.name);
|
setCurrent(theme.name);
|
||||||
stop();
|
stop();
|
||||||
|
} else {
|
||||||
|
start();
|
||||||
}
|
}
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
@@ -215,9 +215,7 @@ function ThemingCard() {
|
|||||||
}
|
}
|
||||||
onChange={(_value, option: ComboboxItem) => {
|
onChange={(_value, option: ComboboxItem) => {
|
||||||
setCurrentThemePlaylist(option.value);
|
setCurrentThemePlaylist(option.value);
|
||||||
if (option.value !== 'Time of Day') {
|
if (option.value === 'Time of Day') {
|
||||||
setThemeName('random');
|
|
||||||
} else {
|
|
||||||
const themeList = JSON.parse(
|
const themeList = JSON.parse(
|
||||||
themePlaylistData.find((t: ThemePlaylist) => t.name === 'Time of Day')
|
themePlaylistData.find((t: ThemePlaylist) => t.name === 'Time of Day')
|
||||||
.themeList,
|
.themeList,
|
||||||
@@ -228,6 +226,8 @@ function ThemingCard() {
|
|||||||
t.id === Number(themeList[Math.floor(new Date().getHours() / 6)]),
|
t.id === Number(themeList[Math.floor(new Date().getHours() / 6)]),
|
||||||
).name,
|
).name,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
setThemeName('random');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Grid,
|
Grid,
|
||||||
GridCol,
|
GridCol,
|
||||||
Group,
|
Group,
|
||||||
|
LoadingOverlay,
|
||||||
Popover,
|
Popover,
|
||||||
Select,
|
Select,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -48,7 +49,11 @@ const ApplicationSettings = () => {
|
|||||||
});
|
});
|
||||||
const appsEndpoint = 'api/applications';
|
const appsEndpoint = 'api/applications';
|
||||||
const appsOrderEndpoint = 'api/applicationOrder';
|
const appsOrderEndpoint = 'api/applicationOrder';
|
||||||
const { data: applicationData, isFetching: isAppsFetching } = useQueryAPI({
|
const {
|
||||||
|
data: applicationData,
|
||||||
|
isFetched: applicationDataIsFetched,
|
||||||
|
isFetching: applicationDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: appsEndpoint,
|
endpoint: appsEndpoint,
|
||||||
});
|
});
|
||||||
const [applications, setApplications] = useState<Application[]>([]);
|
const [applications, setApplications] = useState<Application[]>([]);
|
||||||
@@ -88,7 +93,7 @@ const ApplicationSettings = () => {
|
|||||||
form.reset();
|
form.reset();
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (applicationData && !isAppsFetching) {
|
if (applicationData && applicationDataIsFetched) {
|
||||||
setApplications(
|
setApplications(
|
||||||
applicationData
|
applicationData
|
||||||
.filter(
|
.filter(
|
||||||
@@ -103,7 +108,7 @@ const ApplicationSettings = () => {
|
|||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [applicationData, isAppsFetching, debouncedSearch]);
|
}, [applicationData, applicationDataIsFetched, debouncedSearch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (applicationList.length > 0 && applicationList !== applications) {
|
if (applicationList.length > 0 && applicationList !== applications) {
|
||||||
@@ -305,6 +310,16 @@ const ApplicationSettings = () => {
|
|||||||
mb={isMobile ? '6rem' : '1rem'}
|
mb={isMobile ? '6rem' : '1rem'}
|
||||||
align='center'
|
align='center'
|
||||||
>
|
>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={applicationDataIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{applications.length > 0 ? (
|
{applications.length > 0 ? (
|
||||||
<Droppable droppableId={'groupList'}>
|
<Droppable droppableId={'groupList'}>
|
||||||
{(provided) => (
|
{(provided) => (
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
||||||
import { Box, Button, Card, Group, Stack, Text, TextInput } from '@mantine/core';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Group,
|
||||||
|
LoadingOverlay,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
} from '@mantine/core';
|
||||||
import { isNotEmpty, useForm } from '@mantine/form';
|
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';
|
||||||
@@ -24,7 +33,11 @@ const BookmarkSettings = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const bookmarksEndpoint = 'api/bookmarks';
|
const bookmarksEndpoint = 'api/bookmarks';
|
||||||
const { data: bookmarkData, isFetched: bookmarksIsFetched } = useQueryAPI({
|
const {
|
||||||
|
data: bookmarkData,
|
||||||
|
isFetched: bookmarkDataIsFetched,
|
||||||
|
isFetching: bookmarkDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: bookmarksEndpoint,
|
endpoint: bookmarksEndpoint,
|
||||||
});
|
});
|
||||||
const [bookmarks, setBookmarks] = useState<Bookmark[]>([]);
|
const [bookmarks, setBookmarks] = useState<Bookmark[]>([]);
|
||||||
@@ -68,10 +81,10 @@ const BookmarkSettings = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (bookmarkData && bookmarksIsFetched) {
|
if (bookmarkData && bookmarkDataIsFetched) {
|
||||||
setBookmarks(bookmarkData);
|
setBookmarks(bookmarkData);
|
||||||
}
|
}
|
||||||
}, [bookmarksIsFetched, bookmarkData]);
|
}, [bookmarkDataIsFetched, bookmarkData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (groupData && groupsIsFetched) {
|
if (groupData && groupsIsFetched) {
|
||||||
@@ -83,12 +96,11 @@ const BookmarkSettings = () => {
|
|||||||
a.name.toLowerCase().includes(debouncedGroupSearch.toLowerCase()) ||
|
a.name.toLowerCase().includes(debouncedGroupSearch.toLowerCase()) ||
|
||||||
a.url.toLowerCase().includes(debouncedGroupSearch.toLowerCase()),
|
a.url.toLowerCase().includes(debouncedGroupSearch.toLowerCase()),
|
||||||
)
|
)
|
||||||
.map(
|
.map((a: Bookmark) =>
|
||||||
(a: Bookmark) =>
|
groupData.find(
|
||||||
groupData.filter(
|
(b: BookmarkGroup) =>
|
||||||
(b: BookmarkGroup) =>
|
b.categoryName.toLowerCase() === a.category.toLowerCase(),
|
||||||
b.categoryName.toLowerCase() === a.category.toLowerCase(),
|
),
|
||||||
)[0],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -300,6 +312,16 @@ const BookmarkSettings = () => {
|
|||||||
mb={isMobile ? '6rem' : '1rem'}
|
mb={isMobile ? '6rem' : '1rem'}
|
||||||
align='center'
|
align='center'
|
||||||
>
|
>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={bookmarkDataIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Group w={isMobile ? undefined : '80%'} align='start' justify='center'>
|
<Group w={isMobile ? undefined : '80%'} align='start' justify='center'>
|
||||||
{groups.length > 0 ? (
|
{groups.length > 0 ? (
|
||||||
groups?.map((group: BookmarkGroup) => (
|
groups?.map((group: BookmarkGroup) => (
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
import { Button, Grid, GridCol, Group, Stack, TextInput } from '@mantine/core';
|
import {
|
||||||
|
Button,
|
||||||
|
Grid,
|
||||||
|
GridCol,
|
||||||
|
Group,
|
||||||
|
LoadingOverlay,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
} from '@mantine/core';
|
||||||
import { isNotEmpty, useForm } from '@mantine/form';
|
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';
|
||||||
@@ -10,7 +18,11 @@ import DebouncedMessage from './DebouncedMessage';
|
|||||||
|
|
||||||
const MessageSettings = () => {
|
const MessageSettings = () => {
|
||||||
const endpoint = 'api/messages';
|
const endpoint = 'api/messages';
|
||||||
const { data: messageData, isFetched: isMessagesFetched } = useQueryAPI({
|
const {
|
||||||
|
data: messageData,
|
||||||
|
isFetched: messageDataIsFetched,
|
||||||
|
isFetching: messageDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
@@ -37,7 +49,7 @@ const MessageSettings = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (messageData && isMessagesFetched) {
|
if (messageData && messageDataIsFetched) {
|
||||||
setMessages(
|
setMessages(
|
||||||
messageData.filter((m: Message) =>
|
messageData.filter((m: Message) =>
|
||||||
m.message.toLowerCase().includes(debouncedSearch.toLowerCase()),
|
m.message.toLowerCase().includes(debouncedSearch.toLowerCase()),
|
||||||
@@ -47,7 +59,7 @@ const MessageSettings = () => {
|
|||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [messageData, isMessagesFetched, debouncedSearch]);
|
}, [messageData, messageDataIsFetched, debouncedSearch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(debounced).length) {
|
if (Object.keys(debounced).length) {
|
||||||
@@ -133,7 +145,18 @@ const MessageSettings = () => {
|
|||||||
mt={isMobile ? '10rem' : '6rem'}
|
mt={isMobile ? '10rem' : '6rem'}
|
||||||
mb={isMobile ? '6rem' : undefined}
|
mb={isMobile ? '6rem' : undefined}
|
||||||
align='center'
|
align='center'
|
||||||
|
pos={'relative'}
|
||||||
>
|
>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={messageDataIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Grid w={'75%'}>
|
<Grid w={'75%'}>
|
||||||
{messages.length > 0 ? (
|
{messages.length > 0 ? (
|
||||||
messages?.map((message: Message) => (
|
messages?.map((message: Message) => (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Card, Grid, GridCol, Overlay, Stack, TextInput } from '@mantine/core';
|
import { Card, Grid, GridCol, LoadingOverlay, Stack, TextInput } from '@mantine/core';
|
||||||
import { useDebouncedValue } from '@mantine/hooks';
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
import { SettingsHeader } from 'components';
|
import { SettingsHeader } from 'components';
|
||||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||||
@@ -10,8 +10,8 @@ const PersonalizationSettings = () => {
|
|||||||
const endpoint = 'api/personalData';
|
const endpoint = 'api/personalData';
|
||||||
const {
|
const {
|
||||||
data: personalData,
|
data: personalData,
|
||||||
isFetched: personalIsFetched,
|
isFetched: personalDataIsFetched,
|
||||||
isFetching: personalIsFetching,
|
isFetching: personalDataIsFetching,
|
||||||
} = useQueryAPI({
|
} = useQueryAPI({
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
@@ -22,10 +22,10 @@ const PersonalizationSettings = () => {
|
|||||||
const { isMobile, theme } = useCommonHooks();
|
const { isMobile, theme } = useCommonHooks();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (personalData && personalIsFetched) {
|
if (personalData && personalDataIsFetched) {
|
||||||
setPersonal(personalData[0]);
|
setPersonal(personalData[0]);
|
||||||
}
|
}
|
||||||
}, [personalData, personalIsFetched]);
|
}, [personalData, personalDataIsFetched]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(debounced).length) {
|
if (Object.keys(debounced).length) {
|
||||||
@@ -66,17 +66,18 @@ const PersonalizationSettings = () => {
|
|||||||
isLoading={false}
|
isLoading={false}
|
||||||
setIsLoading={() => {}}
|
setIsLoading={() => {}}
|
||||||
/>
|
/>
|
||||||
<Stack align='center' mt={'6rem'}>
|
<Stack align='center' mt={'6rem'} pos={'relative'}>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={personalDataIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Grid w={'75%'}>
|
<Grid w={'75%'}>
|
||||||
{personalIsFetching && (
|
|
||||||
<Overlay
|
|
||||||
style={{
|
|
||||||
borderRadius: '10px',
|
|
||||||
margin: '-.5rem',
|
|
||||||
backdropFilter: 'blur(5px) brightness(1.2)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<GridCol span={isMobile ? 12 : 4}>
|
<GridCol span={isMobile ? 12 : 4}>
|
||||||
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
|
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import type { SearchHistoryItem } from 'types';
|
|||||||
|
|
||||||
const SearchHistory = () => {
|
const SearchHistory = () => {
|
||||||
const searchEndpoint = 'api/searchHistory';
|
const searchEndpoint = 'api/searchHistory';
|
||||||
const { data: historyData, isFetched: historyDataIsFetched } = useQueryAPI({
|
const {
|
||||||
|
data: historyData,
|
||||||
|
isFetched: historyDataIsFetched,
|
||||||
|
isFetching: historyDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: searchEndpoint,
|
endpoint: searchEndpoint,
|
||||||
});
|
});
|
||||||
const [searchHistory, setSearchHistory] = useState<SearchHistoryItem[]>([]);
|
const [searchHistory, setSearchHistory] = useState<SearchHistoryItem[]>([]);
|
||||||
@@ -92,7 +96,7 @@ const SearchHistory = () => {
|
|||||||
align='center'
|
align='center'
|
||||||
>
|
>
|
||||||
<Stack h={'calc(100vh - 14rem)'} w={'100%'} p={'1rem'} pt={0} mt={'1rem'} gap={0}>
|
<Stack h={'calc(100vh - 14rem)'} w={'100%'} p={'1rem'} pt={0} mt={'1rem'} gap={0}>
|
||||||
<Stack style={{ overflow: 'scroll', whiteSpace: 'none' }}>
|
<Stack h={'inherit'} style={{ overflow: 'scroll', whiteSpace: 'none' }}>
|
||||||
<DataTable
|
<DataTable
|
||||||
data={searchHistory}
|
data={searchHistory}
|
||||||
columns={SearchHistoryColumns(
|
columns={SearchHistoryColumns(
|
||||||
@@ -106,6 +110,7 @@ const SearchHistory = () => {
|
|||||||
filtersEnabled={filtersEnabled}
|
filtersEnabled={filtersEnabled}
|
||||||
globalFilter={globalFilter}
|
globalFilter={globalFilter}
|
||||||
setGlobalFilter={setGlobalFilter}
|
setGlobalFilter={setGlobalFilter}
|
||||||
|
isRefreshing={historyDataIsFetching}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
Grid,
|
Grid,
|
||||||
GridCol,
|
GridCol,
|
||||||
Group,
|
Group,
|
||||||
|
LoadingOverlay,
|
||||||
Popover,
|
Popover,
|
||||||
Select,
|
Select,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -51,11 +52,15 @@ const SearchProviderSettings = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const endpoint = 'api/providers';
|
const endpoint = 'api/providers';
|
||||||
const { data: providerData, isFetched: providerDataIsFetched } = useQueryAPI({
|
const {
|
||||||
|
data: providerData,
|
||||||
|
isFetched: providerDataIsFetched,
|
||||||
|
isFetching: providerDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const [providers, setProviders] = useState<Provider[]>([]);
|
const [providers, setProviders] = useState<Provider[]>([]);
|
||||||
const { isMobile, theme, filter } = 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('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
@@ -273,6 +278,16 @@ const SearchProviderSettings = () => {
|
|||||||
w={'100%'}
|
w={'100%'}
|
||||||
align='center'
|
align='center'
|
||||||
>
|
>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={providerDataIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Grid columns={18} w={'75%'}>
|
<Grid columns={18} w={'75%'}>
|
||||||
{providers.length > 0 ? (
|
{providers.length > 0 ? (
|
||||||
providers?.map((provider: Provider) => (
|
providers?.map((provider: Provider) => (
|
||||||
@@ -291,7 +306,7 @@ const SearchProviderSettings = () => {
|
|||||||
<SearchProviderSettingsItem
|
<SearchProviderSettingsItem
|
||||||
provider={provider}
|
provider={provider}
|
||||||
Icon={allIcons[provider.icon || '']}
|
Icon={allIcons[provider.icon || '']}
|
||||||
filter={filter}
|
filter={iconFilter}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Popover.Target>
|
</Popover.Target>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Group, SimpleGrid, Stack, Text } from '@mantine/core';
|
import { Box, Group, LoadingOverlay, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||||
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
||||||
import { NoItemsFound, SettingsHeader } from 'components';
|
import { NoItemsFound, SettingsHeader } from 'components';
|
||||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||||
@@ -15,7 +15,11 @@ export function ThemePlaylists() {
|
|||||||
endpoint: themesEndpoint,
|
endpoint: themesEndpoint,
|
||||||
});
|
});
|
||||||
const themePlaylistsEndpoint = 'api/themePlaylists';
|
const themePlaylistsEndpoint = 'api/themePlaylists';
|
||||||
const { data: themePlaylistData, isFetched: isThemePlaylistDataFetched } = useQueryAPI({
|
const {
|
||||||
|
data: themePlaylistData,
|
||||||
|
isFetched: themePlaylistDataIsFetched,
|
||||||
|
isFetching: themePlaylistDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: themePlaylistsEndpoint,
|
endpoint: themePlaylistsEndpoint,
|
||||||
});
|
});
|
||||||
const [themePlaylists, setThemePlaylists] = useState<ThemePlaylist[]>([]);
|
const [themePlaylists, setThemePlaylists] = useState<ThemePlaylist[]>([]);
|
||||||
@@ -54,7 +58,7 @@ export function ThemePlaylists() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (themePlaylistData && isThemePlaylistDataFetched) {
|
if (themePlaylistData && themePlaylistDataIsFetched) {
|
||||||
setThemePlaylists(
|
setThemePlaylists(
|
||||||
themePlaylistData.filter(
|
themePlaylistData.filter(
|
||||||
(a: ThemePlaylist) =>
|
(a: ThemePlaylist) =>
|
||||||
@@ -66,7 +70,7 @@ export function ThemePlaylists() {
|
|||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [themePlaylistData, isThemePlaylistDataFetched, debouncedSearch]);
|
}, [themePlaylistData, themePlaylistDataIsFetched, debouncedSearch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
@@ -97,6 +101,16 @@ export function ThemePlaylists() {
|
|||||||
setIsLoading={setIsLoading}
|
setIsLoading={setIsLoading}
|
||||||
/>
|
/>
|
||||||
<Stack mt={isMobile ? '10rem' : '6rem'}>
|
<Stack mt={isMobile ? '10rem' : '6rem'}>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={themePlaylistDataIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{allThemeData && (
|
{allThemeData && (
|
||||||
<>
|
<>
|
||||||
<Group justify='center' align='center' pos={'relative'}>
|
<Group justify='center' align='center' pos={'relative'}>
|
||||||
|
|||||||
@@ -60,11 +60,11 @@ export function ThemeSelect({
|
|||||||
height: '60px',
|
height: '60px',
|
||||||
color: theme?.primaryColor,
|
color: theme?.primaryColor,
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
theme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
theme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? `linear-gradient(${theme?.backgroundColor}33, ${theme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${theme?.backgroundImage})`
|
? undefined
|
||||||
: undefined,
|
: `linear-gradient(${theme?.backgroundColor}33, ${theme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${theme?.backgroundImage})`,
|
||||||
backgroundSize:
|
backgroundSize:
|
||||||
theme?.backgroundImage.toLocaleLowerCase() !== 'none' ? 'cover' : undefined,
|
theme?.backgroundImage.toLocaleLowerCase() === 'none' ? undefined : 'cover',
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
theme?.backgroundImage.toLocaleLowerCase() === 'none'
|
theme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? theme?.backgroundColor
|
? theme?.backgroundColor
|
||||||
@@ -87,13 +87,13 @@ export function ThemeSelect({
|
|||||||
h={'60px'}
|
h={'60px'}
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
theme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
theme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? `linear-gradient(${theme?.backgroundColor}33, ${theme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${theme?.backgroundImage})`
|
? undefined
|
||||||
: undefined,
|
: `linear-gradient(${theme?.backgroundColor}33, ${theme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${theme?.backgroundImage})`,
|
||||||
backgroundSize:
|
backgroundSize:
|
||||||
theme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
theme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? 'cover'
|
? undefined
|
||||||
: undefined,
|
: 'cover',
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
theme?.backgroundImage.toLocaleLowerCase() === 'none'
|
theme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? theme?.backgroundColor
|
? theme?.backgroundColor
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { Box, Button, Group, Overlay, Stack, Text } from '@mantine/core';
|
import { Box, Button, Group, Overlay, Stack, Text } from '@mantine/core';
|
||||||
import {
|
import { useDebouncedState, useDisclosure, useHover } from '@mantine/hooks';
|
||||||
useDebouncedState,
|
|
||||||
useDisclosure,
|
|
||||||
useElementSize,
|
|
||||||
useHover,
|
|
||||||
} from '@mantine/hooks';
|
|
||||||
import { DeleteItemModal } from 'components';
|
import { DeleteItemModal } from 'components';
|
||||||
import { useCommonHooks } from 'hooks';
|
import { useCommonHooks } from 'hooks';
|
||||||
import { textClasses } from 'styles';
|
import { textClasses } from 'styles';
|
||||||
@@ -29,7 +24,6 @@ export function ThemeCard({
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { hovered, ref: hoverRef } = useHover();
|
const { hovered, ref: hoverRef } = useHover();
|
||||||
const { ref, width } = useElementSize();
|
|
||||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
const { currentTheme, setCurrentTheme, setCurrentThemePlaylist, handleDelete } =
|
const { currentTheme, setCurrentTheme, setCurrentThemePlaylist, handleDelete } =
|
||||||
useCommonHooks();
|
useCommonHooks();
|
||||||
@@ -83,7 +77,7 @@ export function ThemeCard({
|
|||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
maxWidth: width,
|
maxWidth: 275,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{cardTheme.name}
|
{cardTheme.name}
|
||||||
@@ -111,19 +105,18 @@ export function ThemeCard({
|
|||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
<Button
|
<Button
|
||||||
ref={ref}
|
|
||||||
w={'275px'}
|
w={'275px'}
|
||||||
h={'115px'}
|
h={'115px'}
|
||||||
disabled={currentTheme === 'random'}
|
disabled={currentTheme === 'random'}
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
cardTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
cardTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? `linear-gradient(${cardTheme?.backgroundColor}33, ${cardTheme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${cardTheme?.backgroundImage})`
|
? undefined
|
||||||
: undefined,
|
: `linear-gradient(${cardTheme?.backgroundColor}33, ${cardTheme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${cardTheme?.backgroundImage})`,
|
||||||
backgroundSize:
|
backgroundSize:
|
||||||
cardTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
cardTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? 'cover'
|
? undefined
|
||||||
: undefined,
|
: 'cover',
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
cardTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
cardTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? cardTheme?.backgroundColor
|
? cardTheme?.backgroundColor
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export function ThemeManagementPill({
|
|||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 3,
|
right: 3,
|
||||||
top: 3,
|
top: 3,
|
||||||
marginRight: !hovered ? 0 : '-2.5rem',
|
marginRight: hovered ? '-2.5rem' : 0,
|
||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
}}
|
}}
|
||||||
align='center'
|
align='center'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ export function ThemeMiniCard({ miniTheme }: { miniTheme: Theme | undefined }) {
|
|||||||
h={'60px'}
|
h={'60px'}
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
miniTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
miniTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? `linear-gradient(${miniTheme?.backgroundColor}33, ${miniTheme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${miniTheme?.backgroundImage})`
|
? undefined
|
||||||
: undefined,
|
: `linear-gradient(${miniTheme?.backgroundColor}33, ${miniTheme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${miniTheme?.backgroundImage})`,
|
||||||
backgroundSize:
|
backgroundSize:
|
||||||
miniTheme?.backgroundImage.toLocaleLowerCase() !== 'none' ? 'cover' : undefined,
|
miniTheme?.backgroundImage.toLocaleLowerCase() === 'none' ? undefined : 'cover',
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
miniTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
miniTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? miniTheme?.backgroundColor
|
? miniTheme?.backgroundColor
|
||||||
|
|||||||
@@ -37,7 +37,87 @@ export function NewThemeStarter({
|
|||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Stack align='center' p={'2rem'}>
|
<Stack align='center' p={'2rem'}>
|
||||||
{!duplicate ? (
|
{duplicate ? (
|
||||||
|
<Stack align='center'>
|
||||||
|
<Group
|
||||||
|
w={'100%'}
|
||||||
|
justify='center'
|
||||||
|
style={{
|
||||||
|
backgroundColor:
|
||||||
|
'color-mix(in srgb, var(--mantine-color-accent-0), var(--mantine-color-background-0))',
|
||||||
|
padding: '1rem',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{allThemes?.map((miniTheme: Theme) => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
{...handlers}
|
||||||
|
key={miniTheme.id}
|
||||||
|
onClick={() => {
|
||||||
|
!pressed && setSelectedTheme(miniTheme);
|
||||||
|
setPressed(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ThemePreviewMiddleware
|
||||||
|
newThemeList={[miniTheme.id as number]}
|
||||||
|
miniTheme={miniTheme}
|
||||||
|
themeList={[]}
|
||||||
|
selectedPlaylist={undefined}
|
||||||
|
setSelectedPlaylist={() => {}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Group
|
||||||
|
style={{
|
||||||
|
backgroundColor:
|
||||||
|
'color-mix(in srgb, var(--mantine-color-primary-0), var(--mantine-color-background-0))',
|
||||||
|
padding: '1rem',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ThemeMiniCard miniTheme={selectedTheme} />
|
||||||
|
<Button
|
||||||
|
disabled={selectedTheme.name === 'default'}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedTheme({
|
||||||
|
...selectedTheme,
|
||||||
|
name: `${selectedTheme.name} copy`,
|
||||||
|
});
|
||||||
|
setIsNew(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
<Button
|
||||||
|
color={'red'}
|
||||||
|
styles={{ label: { color: 'black' } }}
|
||||||
|
onClick={() => {
|
||||||
|
setDuplicate(false);
|
||||||
|
setSelectedTheme({
|
||||||
|
id: undefined,
|
||||||
|
name: 'Default',
|
||||||
|
backgroundImage: 'none',
|
||||||
|
backgroundColor: '#EEEEEE',
|
||||||
|
searchColor: '#00000033',
|
||||||
|
primaryColor: '#222222',
|
||||||
|
accentColor: '#5c5c5c',
|
||||||
|
borderColor: '#5c5c5c',
|
||||||
|
randomEnabled: 1,
|
||||||
|
textShadowsEnabled: 0,
|
||||||
|
appShadowsEnabled: 0,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
|
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
|
||||||
Please choose an option:
|
Please choose an option:
|
||||||
@@ -189,86 +269,6 @@ export function NewThemeStarter({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<Stack align='center'>
|
|
||||||
<Group
|
|
||||||
w={'100%'}
|
|
||||||
justify='center'
|
|
||||||
style={{
|
|
||||||
backgroundColor:
|
|
||||||
'color-mix(in srgb, var(--mantine-color-accent-0), var(--mantine-color-background-0))',
|
|
||||||
padding: '1rem',
|
|
||||||
borderRadius: '8px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{allThemes?.map((miniTheme: Theme) => {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
{...handlers}
|
|
||||||
key={miniTheme.id}
|
|
||||||
onClick={() => {
|
|
||||||
!pressed && setSelectedTheme(miniTheme);
|
|
||||||
setPressed(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ThemePreviewMiddleware
|
|
||||||
newThemeList={[miniTheme.id as number]}
|
|
||||||
miniTheme={miniTheme}
|
|
||||||
themeList={[]}
|
|
||||||
selectedPlaylist={undefined}
|
|
||||||
setSelectedPlaylist={() => {}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<Group
|
|
||||||
style={{
|
|
||||||
backgroundColor:
|
|
||||||
'color-mix(in srgb, var(--mantine-color-primary-0), var(--mantine-color-background-0))',
|
|
||||||
padding: '1rem',
|
|
||||||
borderRadius: '8px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ThemeMiniCard miniTheme={selectedTheme} />
|
|
||||||
<Button
|
|
||||||
disabled={selectedTheme.name === 'default'}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedTheme({
|
|
||||||
...selectedTheme,
|
|
||||||
name: `${selectedTheme.name} copy`,
|
|
||||||
});
|
|
||||||
setIsNew(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Confirm
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
<Button
|
|
||||||
color={'red'}
|
|
||||||
styles={{ label: { color: 'black' } }}
|
|
||||||
onClick={() => {
|
|
||||||
setDuplicate(false);
|
|
||||||
setSelectedTheme({
|
|
||||||
id: undefined,
|
|
||||||
name: 'Default',
|
|
||||||
backgroundImage: 'none',
|
|
||||||
backgroundColor: '#EEEEEE',
|
|
||||||
searchColor: '#00000033',
|
|
||||||
primaryColor: '#222222',
|
|
||||||
accentColor: '#5c5c5c',
|
|
||||||
borderColor: '#5c5c5c',
|
|
||||||
randomEnabled: 1,
|
|
||||||
textShadowsEnabled: 0,
|
|
||||||
appShadowsEnabled: 0,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function ThemeEditor({
|
|||||||
? `linear-gradient(${selectedTheme.backgroundColor}88, ${selectedTheme.backgroundColor}88), no-repeat center url(../../api/backgroundImages/thumbs/${item})`
|
? `linear-gradient(${selectedTheme.backgroundColor}88, ${selectedTheme.backgroundColor}88), no-repeat center url(../../api/backgroundImages/thumbs/${item})`
|
||||||
: `no-repeat center url(../../api/backgroundImages/thumbs/${item})`
|
: `no-repeat center url(../../api/backgroundImages/thumbs/${item})`
|
||||||
: 'repeating-linear-gradient(45deg, #33333366, #33333366 10px, #88888866 10px, #88888866 20px)',
|
: 'repeating-linear-gradient(45deg, #33333366, #33333366 10px, #88888866 10px, #88888866 20px)',
|
||||||
backgroundSize: item !== 'none' ? 'cover' : undefined,
|
backgroundSize: item === 'none' ? undefined : 'cover',
|
||||||
backgroundColor: item === 'none' ? '#00000088' : undefined,
|
backgroundColor: item === 'none' ? '#00000088' : undefined,
|
||||||
paddingLeft: '.5rem',
|
paddingLeft: '.5rem',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -639,12 +639,12 @@ export function ThemeEditor({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
!duplicate
|
duplicate
|
||||||
? setSelectedTheme({ ...defaultTheme })
|
? setSelectedTheme({
|
||||||
: setSelectedTheme({
|
|
||||||
...defaultTheme,
|
...defaultTheme,
|
||||||
name: `${defaultTheme.name} copy`,
|
name: `${defaultTheme.name} copy`,
|
||||||
});
|
})
|
||||||
|
: setSelectedTheme({ ...defaultTheme });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Reset Changes
|
Reset Changes
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
|
|||||||
<Stack
|
<Stack
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
selectedTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
selectedTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? `linear-gradient(${selectedTheme?.backgroundColor}33, ${selectedTheme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${selectedTheme?.backgroundImage})`
|
? undefined
|
||||||
: undefined,
|
: `linear-gradient(${selectedTheme?.backgroundColor}33, ${selectedTheme?.backgroundColor}33), url(../../api/backgroundImages/thumbs/${selectedTheme?.backgroundImage})`,
|
||||||
backgroundSize:
|
backgroundSize:
|
||||||
selectedTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
|
selectedTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? 'cover'
|
? undefined
|
||||||
: undefined,
|
: 'cover',
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
selectedTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
selectedTheme?.backgroundImage.toLocaleLowerCase() === 'none'
|
||||||
? selectedTheme?.backgroundColor
|
? selectedTheme?.backgroundColor
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ export function ThemePreviewMiddleware({
|
|||||||
: undefined,
|
: undefined,
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!pressed) {
|
if (pressed) {
|
||||||
|
setPressed(false);
|
||||||
|
} else {
|
||||||
const newSelectedPlaylist = { ...selectedPlaylist };
|
const newSelectedPlaylist = { ...selectedPlaylist };
|
||||||
if (themeList?.includes(miniTheme.id)) {
|
if (themeList?.includes(miniTheme.id)) {
|
||||||
newThemeList.splice(themeList.indexOf(miniTheme.id), 1);
|
newThemeList.splice(themeList.indexOf(miniTheme.id), 1);
|
||||||
@@ -49,8 +51,6 @@ export function ThemePreviewMiddleware({
|
|||||||
}
|
}
|
||||||
newSelectedPlaylist.themeList = JSON.stringify(newThemeList);
|
newSelectedPlaylist.themeList = JSON.stringify(newThemeList);
|
||||||
setSelectedPlaylist(newSelectedPlaylist);
|
setSelectedPlaylist(newSelectedPlaylist);
|
||||||
} else {
|
|
||||||
setPressed(false);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Group,
|
Group,
|
||||||
|
LoadingOverlay,
|
||||||
Overlay,
|
Overlay,
|
||||||
Stack,
|
Stack,
|
||||||
Switch,
|
Switch,
|
||||||
@@ -24,11 +25,19 @@ import { ThemeModal } from './ThemeModal';
|
|||||||
|
|
||||||
export function Themes() {
|
export function Themes() {
|
||||||
const backgroundsEndpoint = 'api/backgrounds';
|
const backgroundsEndpoint = 'api/backgrounds';
|
||||||
const { data: allThemeData, isFetched: isAllThemeDataFetched } = useQueryAPI({
|
const {
|
||||||
|
data: allThemeData,
|
||||||
|
isFetched: allThemeDataIsFetched,
|
||||||
|
isFetching: allThemeDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: 'api/themes',
|
endpoint: 'api/themes',
|
||||||
});
|
});
|
||||||
const [allThemes, setAllThemes] = useState<Theme[]>([]);
|
const [allThemes, setAllThemes] = useState<Theme[]>([]);
|
||||||
const { data: backgroundData, isFetched: isBackgroundFetched } = useQueryAPI({
|
const {
|
||||||
|
data: backgroundData,
|
||||||
|
isFetched: backgroundDataIsFetched,
|
||||||
|
isFetching: backgroundDataIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: backgroundsEndpoint,
|
endpoint: backgroundsEndpoint,
|
||||||
});
|
});
|
||||||
const { data: themePlaylistData } = useQueryAPI({
|
const { data: themePlaylistData } = useQueryAPI({
|
||||||
@@ -76,13 +85,13 @@ export function Themes() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (backgroundData && isBackgroundFetched) {
|
if (backgroundData && backgroundDataIsFetched) {
|
||||||
setBackgroundList([
|
setBackgroundList([
|
||||||
'None',
|
'None',
|
||||||
...backgroundData.sort((a: string, b: string) => a.localeCompare(b)),
|
...backgroundData.sort((a: string, b: string) => a.localeCompare(b)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}, [backgroundData, isBackgroundFetched]);
|
}, [backgroundData, backgroundDataIsFetched]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (themePlaylistData) {
|
if (themePlaylistData) {
|
||||||
@@ -91,7 +100,7 @@ export function Themes() {
|
|||||||
}, [themePlaylistData]);
|
}, [themePlaylistData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (allThemeData && isAllThemeDataFetched) {
|
if (allThemeData && allThemeDataIsFetched) {
|
||||||
setAllThemes(
|
setAllThemes(
|
||||||
allThemeData.filter((a: Theme) =>
|
allThemeData.filter((a: Theme) =>
|
||||||
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()),
|
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()),
|
||||||
@@ -101,7 +110,7 @@ export function Themes() {
|
|||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [allThemeData, isAllThemeDataFetched, debouncedSearch]);
|
}, [allThemeData, allThemeDataIsFetched, debouncedSearch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (allThemes) {
|
if (allThemes) {
|
||||||
@@ -156,6 +165,16 @@ export function Themes() {
|
|||||||
setIsLoading={setIsLoading}
|
setIsLoading={setIsLoading}
|
||||||
/>
|
/>
|
||||||
<Stack mt={isMobile ? '11rem' : '7rem'} align='center'>
|
<Stack mt={isMobile ? '11rem' : '7rem'} align='center'>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={allThemeDataIsFetching || backgroundDataIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Group
|
<Group
|
||||||
w={'85%'}
|
w={'85%'}
|
||||||
justify='center'
|
justify='center'
|
||||||
@@ -201,10 +220,10 @@ export function Themes() {
|
|||||||
zIndex:
|
zIndex:
|
||||||
currentTheme === 'random' &&
|
currentTheme === 'random' &&
|
||||||
(themePlaylists
|
(themePlaylists
|
||||||
.filter(
|
.find(
|
||||||
(t: ThemePlaylist) =>
|
(t: ThemePlaylist) =>
|
||||||
t.name.toLowerCase() === currentThemePlaylist.toLowerCase(),
|
t.name.toLowerCase() === currentThemePlaylist.toLowerCase(),
|
||||||
)[0]
|
)
|
||||||
?.themeList.includes(`${cardTheme.id}`) ||
|
?.themeList.includes(`${cardTheme.id}`) ||
|
||||||
cardTheme.name === theme.name)
|
cardTheme.name === theme.name)
|
||||||
? 195
|
? 195
|
||||||
@@ -222,10 +241,7 @@ export function Themes() {
|
|||||||
</Group>
|
</Group>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<Group
|
<Group w={'100%'} style={{ display: 'flex', justifyContent: 'center' }}>
|
||||||
w={'100%'}
|
|
||||||
style={{ zIndex: 1000, display: 'flex', justifyContent: 'center' }}
|
|
||||||
>
|
|
||||||
<NoItemsFound />
|
<NoItemsFound />
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
import { Button, Grid, GridCol, Group, Popover, Stack, TextInput } from '@mantine/core';
|
import {
|
||||||
|
Button,
|
||||||
|
Grid,
|
||||||
|
GridCol,
|
||||||
|
Group,
|
||||||
|
LoadingOverlay,
|
||||||
|
Popover,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
} from '@mantine/core';
|
||||||
import { isNotEmpty, matches, useForm } from '@mantine/form';
|
import { isNotEmpty, matches, 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';
|
||||||
@@ -26,7 +35,11 @@ const WolClientSettings = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const endpoint = 'api/wolClients';
|
const endpoint = 'api/wolClients';
|
||||||
const { data: wolClientsData, isFetched: wolClientsIsFetched } = useQueryAPI({
|
const {
|
||||||
|
data: wolClientsData,
|
||||||
|
isFetched: wolClientsIsFetched,
|
||||||
|
isFetching: wolClientsIsFetching,
|
||||||
|
} = useQueryAPI({
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
});
|
});
|
||||||
const [wolClients, setWolClients] = useState<WolClient[]>([]);
|
const [wolClients, setWolClients] = useState<WolClient[]>([]);
|
||||||
@@ -145,6 +158,16 @@ const WolClientSettings = () => {
|
|||||||
w={'100%'}
|
w={'100%'}
|
||||||
align='center'
|
align='center'
|
||||||
>
|
>
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={wolClientsIsFetching}
|
||||||
|
zIndex={180}
|
||||||
|
overlayProps={{ blur: 5 }}
|
||||||
|
loaderProps={{
|
||||||
|
color: 'var(--mantine-color-primary-0)',
|
||||||
|
type: 'bars',
|
||||||
|
size: 'xl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Grid columns={18} w={'75%'}>
|
<Grid columns={18} w={'75%'}>
|
||||||
{wolClients.length > 0 ? (
|
{wolClients.length > 0 ? (
|
||||||
wolClients?.map((client: WolClient) => (
|
wolClients?.map((client: WolClient) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user