Slight refactor

This commit is contained in:
Sean Edmunds
2025-01-17 16:16:31 -06:00
parent eef30a84e3
commit 265981bee7
27 changed files with 398 additions and 352 deletions

View File

@@ -4,9 +4,8 @@ import {
createTheme,
mergeMantineTheme,
} from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';
import { ModalsProvider } from '@mantine/modals';
import { useQueryAPI } from 'hooks';
import { useCommonHooks, useQueryAPI } from 'hooks';
import { useEffect } from 'react';
import {
Navigate,
@@ -20,66 +19,47 @@ import '@mantine/core/styles.css'; //import Mantine V7 styles needed by MRT
import '@mantine/dates/styles.css'; //if using mantine date picker features
import 'mantine-react-table/styles.css'; //import MRT styles
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { appTheme } from 'contexts';
import { useAtom } from 'jotai';
import './index.css';
import { Homepage, Settings } from 'pages';
function App() {
const { data, isFetched } = useQueryAPI({
const { data: allThemes, isFetched: isAllThemesFetched } = useQueryAPI({
endpoint: 'api/themes',
});
const [themeName, _] = useLocalStorage<any>({
key: 'theme',
defaultValue: 'blackboard',
});
const [theme, setTheme] = useAtom(appTheme);
const { theme, setTheme, themeName, handleThemeChange } = useCommonHooks();
useEffect(() => {
if (data && themeName) {
if (allThemes && themeName) {
if (themeName !== 'random') {
const temp = data.find((t: any) => t.name === themeName);
const temp = allThemes.find((t: any) => t.name === themeName);
setTheme(temp);
} else {
const enabled = data.filter((t: any) => t.randomEnabled);
if (enabled.length === 0) {
setTheme(data[0]);
} else {
const index = Math.floor(Math.random() * enabled.length);
setTheme(enabled[index]);
}
handleThemeChange(allThemes, false);
}
}
}, [isFetched, themeName]);
}, [isAllThemesFetched, themeName]);
const router = createBrowserRouter(
createRoutesFromElements(
<Route>
<Route path='' element={<MainLayout theme={theme} />}>
<Route path='' element={<MainLayout />}>
<Route
path='/'
element={<Navigate to={`/${import.meta.env.VITE_BASE_URL}`} />}
/>
<Route
path={`/${import.meta.env.VITE_BASE_URL}`}
element={<Homepage theme={theme} />}
/>
<Route path={`/${import.meta.env.VITE_BASE_URL}`} element={<Homepage />} />
<Route
path={`/${import.meta.env.VITE_BASE_URL}/settings`}
element={<Settings theme={theme} setTheme={setTheme} allThemes={data} />}
element={<Settings />}
/>
</Route>
</Route>,
),
);
const overrides = createTheme({
white: theme?.backgroundColor,
fontFamily: '-apple-system,Jost,BlinkMacSystemFont,Helvetica Neue,Roboto,sans-serif',
});
const mantineTheme = mergeMantineTheme(DEFAULT_THEME, overrides);
return (
<>
{theme && (

View File

@@ -9,9 +9,7 @@ import '../../../src/dataTableStyles.css';
import { ActionIcon, Button, Flex, Text } from '@mantine/core';
import { modals } from '@mantine/modals';
import { IconEdit, IconTrash } from '@tabler/icons-react';
import { appTheme } from 'contexts';
import { useMutationAPI } from 'hooks';
import { useAtom } from 'jotai';
import { useCommonHooks, useMutationAPI } from 'hooks';
import { FiPlus } from 'react-icons/fi';
import { actionIconClasses, buttonClasses } from 'styles';
@@ -30,7 +28,7 @@ export function DataTableComponent({
isLoading: boolean;
endpoint: string;
}) {
const [theme, _] = useAtom(appTheme);
const { theme } = useCommonHooks();
const { mutateAsync: createRow } = useMutationAPI();
const { mutateAsync: editRow } = useMutationAPI();
const { mutateAsync: deleteRow } = useMutationAPI();
@@ -112,7 +110,6 @@ export function DataTableComponent({
},
},
},
renderCreateRowModalContent: ({ row }) => undefined,
renderTopToolbarCustomActions: ({ table }) => {
return (
<>

View File

@@ -1,17 +1,11 @@
import { Divider, Group, Modal, Stack, Text } from '@mantine/core';
import { appTheme } from 'contexts';
import { useAtom } from 'jotai';
import { useCommonHooks } from 'hooks';
import { modalClasses } from 'styles';
export function HelpModal({
setValue,
setProviderLabel,
setProviderURL,
providers,
providerModalOpened,
closeProviderModal,
}: any) {
const [theme, _] = useAtom(appTheme);
export function HelpModal({ providerModalOpened, closeProviderModal }: any) {
const { theme, providers, setSearchValue, setProviderLabel, setProviderURL } =
useCommonHooks();
return (
<Modal.Root
size={'60%'}
@@ -20,7 +14,7 @@ export function HelpModal({
opened={providerModalOpened}
onClose={() => {
closeProviderModal();
setValue('');
setSearchValue('');
setProviderLabel('Google Search');
setProviderURL('https://www.google.com/search?q=');
}}

View File

@@ -1,7 +1,9 @@
import { Box } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { Outlet } from 'react-router-dom';
export function MainLayout({ theme }: any) {
export function MainLayout() {
const { theme } = useCommonHooks();
return (
<Box
style={{

View File

@@ -1,5 +1,6 @@
import { Icon } from '@iconify/react/dist/iconify.js';
import { Box, Card, Center, Group, Image, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
export enum WindDirection {
N = 0,
@@ -12,7 +13,8 @@ export enum WindDirection {
NW = 315,
}
export default function HourlyCard({ weather, isMobile }: any) {
export default function HourlyCard({ weather }: any) {
const { isMobile } = useCommonHooks();
return (
<Stack gap={0} align='center' mx={isMobile ? '.25rem' : 0}>
<Card

View File

@@ -1,28 +1,22 @@
import { Box, Center, Divider, Group, Modal, Stack } from '@mantine/core';
import { useAtom } from 'jotai';
import { modalClasses } from 'styles';
import SmallOutlookCard from './SmallOutlookCard';
import SmallWeatherCard from './SmallWeatherCard';
import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react';
import { appTheme } from '../../contexts/theme';
import HourlyCard from './HourlyCard';
export function WeatherModal({
current,
outlook,
hourly,
closeWeatherModal,
weatherModalOpened,
setProviderLabel,
setProviderURL,
setValue,
isMobile,
}: any) {
const [theme, _] = useAtom(appTheme);
const [hourlyForecast, setHourlyForecast] = useState<any>([]);
const [sunset, setSunset] = useState(100);
const { theme, isMobile, hourly, setProviderLabel, setSearchValue, setProviderURL } =
useCommonHooks();
useEffect(() => {
if (hourly) {
setHourlyForecast(hourly.properties.periods.slice(1, 13));
@@ -51,7 +45,7 @@ export function WeatherModal({
onClose={() => {
closeWeatherModal();
setProviderLabel('Google Search');
setValue('');
setSearchValue('');
setProviderURL('https://www.google.com/search?q=');
}}
style={{
@@ -119,9 +113,7 @@ export function WeatherModal({
>
{hourlyForecast.length > 0 &&
hourlyForecast.map((h: any) => {
return (
<HourlyCard key={h.number} weather={h} isMobile={isMobile} />
);
return <HourlyCard key={h.number} weather={h} />;
})}
</Box>
</Group>

View File

@@ -45,3 +45,11 @@ export function containsProtocol(str: any) {
const pattern = /^(https?:\/\/).*/i;
return !!pattern.test(str);
}
export function runWakeCommand() {
const requestOptions = {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
};
fetch(`/${import.meta.env.VITE_BASE_URL}/api/wakeDakota`, requestOptions);
}

View File

@@ -1,3 +1,3 @@
export { useQueryAPI, useWeatherAPI, useMutationAPI } from './useAPI';
export { date, greet } from './helpers';
export { validURL, containsProtocol } from './helpers';
export { date, greet, runWakeCommand, validURL, containsProtocol } from './helpers';
export { useCommonHooks } from './useCommonHooks';

204
src/hooks/useCommonHooks.ts Normal file
View File

@@ -0,0 +1,204 @@
import { useLocalStorage } from '@mantine/hooks';
import { appTheme } from 'contexts';
import { useAtom } from 'jotai';
import { useEffect, useState } from 'react';
import { useQueryAPI, useWeatherAPI } from './useAPI';
export function useCommonHooks() {
const isMobile = window.matchMedia('(max-width: 600px)').matches;
const {
data: apps,
isFetching: isAppsFetching,
isLoading: isAppsLoading,
} = useQueryAPI({ endpoint: 'api/apps' });
const { data: allThemes, isFetched: isAllThemesFetched } = useQueryAPI({
endpoint: 'api/themes',
});
const { data: backgrounds, isFetched: isBackgroundFetched } = useQueryAPI({
endpoint: 'api/backgrounds',
});
const {
data: messages,
isFetching: isMessagesFetching,
isFetched: isMessagesFetched,
isLoading: isMessagesLoading,
} = useQueryAPI({
endpoint: 'api/messages',
});
const {
data: linkData,
isFetched: linksIsFetched,
isFetching: linksIsFetching,
isLoading: linksIsLoading,
} = useQueryAPI({
endpoint: 'api/links',
});
const {
data: groupData,
isFetched: groupsIsFetched,
isFetching: groupsIsFetching,
isLoading: groupsIsLoading,
} = useQueryAPI({
endpoint: 'api/categories',
});
const {
data: personal,
isFetched: personalIsFetched,
isFetching: personalIsFetching,
isLoading: personalIsLoading,
} = useQueryAPI({
endpoint: 'api/personalData',
});
const {
data: providers,
isFetched: providersIsFetched,
isFetching: providersIsFetching,
isLoading: providersIsLoading,
} = useQueryAPI({ endpoint: 'api/providers' });
const { data: basicWeather } = useWeatherAPI({ endpoint: 'baseline' });
const { data: weather } = useWeatherAPI({ endpoint: 'current' });
const { data: forecast } = useWeatherAPI({ endpoint: 'forecast' });
const { data: hourly } = useWeatherAPI({ endpoint: 'hourly' });
const [theme, setTheme] = useAtom(appTheme);
const [themeName, setThemeName] = useLocalStorage<any>({
key: 'theme',
defaultValue: 'blackboard',
});
const [appFilter, setAppFilter] = useLocalStorage<[any]>({
key: 'appFilter',
defaultValue: ['All'],
});
const [bookmarkFilter, setBookmarkFilter] = useLocalStorage<[any]>({
key: 'bookmarkFilter',
defaultValue: ['All'],
});
const [currentTheme, setCurrentTheme] = useLocalStorage<any>({
key: 'theme',
defaultValue: 'blackboard',
});
const [searchValue, setSearchValue] = useState('');
const [providerLabel, setProviderLabel] = useState('Google Search');
const [providerURL, setProviderURL] = useState('https://www.google.com/search?q=');
const handleThemeChange = (themes: any, save: any) => {
const enabled = themes
.filter((t: any) => t.randomEnabled)
.filter((t: any) => t.name !== theme.name);
if (enabled.length === 0) {
setTheme(themes[0]);
save && setThemeName(themes[0].name);
} else {
const index = Math.floor(Math.random() * enabled.length);
setTheme(enabled[index]);
save && setThemeName(enabled[index].name);
}
};
const [applicationData, setApplicationData] = useState<any>([]);
useEffect(() => {
if (apps && !isAppsFetching) {
setApplicationData(apps);
}
}, [apps, isAppsFetching]);
const [bookmarkData, setBookmarkData] = useState<any>([]);
const [groups, setGroups] = useState([]);
useEffect(() => {
if (linkData && linksIsFetched) {
setBookmarkData(linkData);
}
}, [linksIsFetched, linkData]);
useEffect(() => {
if (groupData && groupsIsFetched) {
const groupList = groupData?.map((category: any) => {
return category.categoryName;
});
setGroups(groupList);
}
}, [groupData, groupsIsFetched]);
const [messageData, setMessageData] = useState<any>([]);
useEffect(() => {
if (messages && isMessagesFetched) {
setMessageData(messages);
}
}, [messages, isMessagesFetched]);
const [personalData, setPersonalData] = useState<any>([]);
useEffect(() => {
if (personal && personalIsFetched) {
setPersonalData(personal);
}
}, [personal, personalIsFetched]);
const [providerData, setProviderData] = useState<any>([]);
useEffect(() => {
if (providers && providersIsFetched) {
setProviderData(providers);
}
}, [providers, providersIsFetched]);
return {
isMobile,
theme,
setTheme,
allThemes,
backgrounds,
isBackgroundFetched,
isAllThemesFetched,
messages,
isMessagesFetching,
basicWeather,
weather,
forecast,
hourly,
handleThemeChange,
themeName,
setThemeName,
appFilter,
setAppFilter,
bookmarkFilter,
setBookmarkFilter,
currentTheme,
setCurrentTheme,
searchValue,
setSearchValue,
providerLabel,
setProviderLabel,
providerURL,
setProviderURL,
providers,
providerData,
providersIsFetched,
providersIsFetching,
providersIsLoading,
linkData,
linksIsFetched,
groupData,
groupsIsFetched,
apps,
isAppsFetching,
isAppsLoading,
applicationData,
bookmarkData,
groups,
linksIsFetching,
linksIsLoading,
groupsIsFetching,
groupsIsLoading,
messageData,
isMessagesFetched,
isMessagesLoading,
personal,
personalData,
personalIsFetched,
personalIsFetching,
personalIsLoading,
};
}

View File

@@ -1,6 +1,6 @@
import { Stack } from '@mantine/core';
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { date, greet, useQueryAPI, useWeatherAPI } from 'hooks';
import { useDisclosure } from '@mantine/hooks';
import { date, greet, useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react';
import { stackClasses } from 'styles';
import type { CurrentWeather, OutlookWeather } from 'types';
@@ -10,21 +10,18 @@ import Header from './components/Header/Header';
import Search from './components/Search/Search';
import SettingsButton from './components/SettingsButton';
export function Homepage({ theme }: any) {
const [appFilter, setAppFilter] = useLocalStorage<[any]>({
key: 'appFilter',
defaultValue: ['All'],
});
const [bookmarkFilter, setBookmarkFilter] = useLocalStorage<[any]>({
key: 'bookmarkFilter',
defaultValue: ['All'],
});
const { data, isFetching } = useQueryAPI({ endpoint: 'api/messages' });
const { data: basicWeather } = useWeatherAPI({ endpoint: 'baseline' });
const { data: weather } = useWeatherAPI({ endpoint: 'current' });
const { data: forecast } = useWeatherAPI({ endpoint: 'forecast' });
const { data: hourly } = useWeatherAPI({ endpoint: 'hourly' });
export function Homepage() {
const [displayedGreeting, setDisplayedGreeting] = useState('');
const {
appFilter,
bookmarkFilter,
isMobile,
messages,
isMessagesFetching,
weather,
basicWeather,
forecast,
} = useCommonHooks();
const [current, setCurrent] = useState<CurrentWeather>({
icon: '',
temperature: 0,
@@ -47,17 +44,14 @@ export function Homepage({ theme }: any) {
windDirection: 'N',
},
]);
const isMobile = window.matchMedia('(max-width: 600px)').matches;
const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
useDisclosure(false);
const [providerLabel, setProviderLabel] = useState('Google Search');
const [providerURL, setProviderURL] = useState('https://www.google.com/search?q=');
const [value, setValue] = useState('');
useEffect(() => {
if (data) {
setDisplayedGreeting(greet(data));
if (messages) {
setDisplayedGreeting(greet(messages));
}
}, [data, isFetching]);
}, [messages, isMessagesFetching]);
useEffect(() => {
if (weather && basicWeather) {
@@ -119,19 +113,7 @@ export function Homepage({ theme }: any) {
<Stack gap={0} className={stackClasses.containerStack} justify='center'>
<Search
current={current}
setCurrent={setCurrent}
outlook={outlook}
setOutlook={setOutlook}
weather={weather}
basicWeather={basicWeather}
forecast={forecast}
hourly={hourly}
providerLabel={providerLabel}
setProviderLabel={setProviderLabel}
providerURL={providerURL}
setProviderURL={setProviderURL}
value={value}
setValue={setValue}
weatherModalOpened={weatherModalOpened}
closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal}
@@ -139,30 +121,16 @@ export function Homepage({ theme }: any) {
<Header
date={date(isMobile)}
greet={displayedGreeting}
isMobile={isMobile}
current={current}
setCurrent={setCurrent}
outlook={outlook}
weather={weather}
basicWeather={basicWeather}
forecast={forecast}
hourly={hourly}
setProviderLabel={setProviderLabel}
setProviderURL={setProviderURL}
setValue={setValue}
weatherModalOpened={weatherModalOpened}
closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal}
/>
<Applications contentType={appFilter} isMobile={isMobile} />
<Bookmarks contentType={bookmarkFilter} isMobile={isMobile} />
<SettingsButton
theme={theme}
setAppFilter={setAppFilter}
appFilter={appFilter}
setBookmarkFilter={setBookmarkFilter}
bookmarkFilter={bookmarkFilter}
/>
<Applications contentType={appFilter} />
<Bookmarks contentType={bookmarkFilter} />
<SettingsButton />
</Stack>
);
}

View File

@@ -1,10 +1,12 @@
import { Icon } from '@iconify/react';
import { Box, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { useState } from 'react';
import { stackClasses, textClasses } from 'styles';
function AppItem({ url, icon, name, isMobile }: any) {
function AppItem({ url, icon, name }: any) {
const [textDecoration, setTextDecoration] = useState<string>('none');
const { isMobile } = useCommonHooks();
return (
<Box
@@ -15,11 +17,11 @@ function AppItem({ url, icon, name, isMobile }: any) {
className={stackClasses.appItemBox}
>
<Stack
align={isMobile && 'center'}
justify={isMobile && 'center'}
align={isMobile ? 'center' : undefined}
justify={isMobile ? 'center' : undefined}
style={{
height: '64px',
width: isMobile && '100%',
width: isMobile ? '100%' : undefined,
marginRight: isMobile ? '0' : '1em',
marginLeft: isMobile ? '0' : '1em',
paddingTop: isMobile ? '0' : '15px',

View File

@@ -1,20 +1,13 @@
import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useCommonHooks } from 'hooks';
import { textClasses } from 'styles';
import AppItem from './AppItem';
function Applications({ contentType, isMobile }: any) {
const { data, isFetching } = useQueryAPI({ endpoint: 'api/apps' });
const [applications, setApplications] = useState([]);
useEffect(() => {
if (data && !isFetching) {
setApplications(data);
}
}, [data, isFetching]);
function Applications({ contentType }: any) {
const { isMobile, applicationData } = useCommonHooks();
return (
<>
{applications.length > 0 && contentType.length > 0 && (
{applicationData.length > 0 && contentType.length > 0 && (
<Stack id='apps' style={{ gap: 0 }}>
<Text className={textClasses.sectionTitle} classNames={textClasses}>
Applications
@@ -32,7 +25,7 @@ function Applications({ contentType, isMobile }: any) {
paddingBottom: 'var(--module-spacing)',
}}
>
{applications?.map((application: any) => {
{applicationData?.map((application: any) => {
if (
contentType.includes(application.filterName) ||
contentType.includes('All')
@@ -44,7 +37,6 @@ function Applications({ contentType, isMobile }: any) {
url={application.url}
icon={application.icon}
category={application.category}
isMobile={isMobile}
/>
);
})}

View File

@@ -1,30 +1,16 @@
import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useCommonHooks } from 'hooks';
import { textClasses } from 'styles';
import LinkGroups from './LinkGroups';
function Links({ contentType, isMobile }: any) {
const { data: linkData, isFetched: linksIsFetched } = useQueryAPI({
endpoint: 'api/links',
});
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
endpoint: 'api/categories',
});
const [bookmarks, setBookmarks] = useState([]);
const [categories, setCategories] = useState([]);
useEffect(() => {
if (linksIsFetched && groupsIsFetched) {
setBookmarks(linkData);
setCategories(groupData);
}
}, [linksIsFetched, groupsIsFetched]);
function Links({ contentType }: any) {
const { isMobile, linksIsFetched, groupsIsFetched, groups, bookmarkData } =
useCommonHooks();
return (
<>
{groupsIsFetched &&
linksIsFetched &&
categories?.length > 0 &&
groups?.length > 0 &&
contentType.length > 0 && (
<Stack id='links' style={{ gap: 0 }}>
<Text className={textClasses.sectionTitle} classNames={textClasses}>
@@ -40,19 +26,18 @@ function Links({ contentType, isMobile }: any) {
gridTemplateRows: 'auto',
}}
>
{categories.map((category: any) => {
{groups.map((category: any) => {
if (
contentType.includes(category.filterName) ||
contentType.includes('All')
) {
return (
<LinkGroups
key={category.id}
key={category}
category={category.categoryName}
links={bookmarks.filter(
(bookmark: any) => bookmark.category === category.categoryName,
links={bookmarkData.filter(
(bookmark: any) => bookmark.category === category,
)}
isMobile={isMobile}
/>
);
}

View File

@@ -2,7 +2,7 @@ import { Stack, Text } from '@mantine/core';
import { textClasses } from 'styles';
import LinkItem from './LinkItem';
function LinkGroups({ links, category, isMobile }: any) {
function LinkGroups({ links, category }: any) {
return (
<Stack
style={{
@@ -16,7 +16,7 @@ function LinkGroups({ links, category, isMobile }: any) {
{category}
</Text>
{links?.map((link: any) => (
<LinkItem key={link.name} link={link} isMobile={isMobile} />
<LinkItem key={link.name} link={link} />
))}
</Stack>
);

View File

@@ -1,25 +1,20 @@
import { Group, Stack, Text } from '@mantine/core';
import { WeatherModal } from 'components';
import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react';
import { textClasses } from 'styles';
function Header({
greet,
date,
isMobile,
current,
setCurrent,
weather,
basicWeather,
outlook,
hourly,
weatherModalOpened,
closeWeatherModal,
openWeatherModal,
setProviderLabel,
setProviderURL,
setValue,
}: any) {
const { isMobile, weather, basicWeather } = useCommonHooks();
const [time, setTime] = useState(new Date());
function fixDigits(k: any) {
if (k < 10) {
@@ -80,7 +75,7 @@ function Header({
<Text
className={textClasses.greeting}
classNames={textClasses}
fz={isMobile && 24}
fz={isMobile ? 24 : undefined}
>
{greet}
</Text>
@@ -152,13 +147,8 @@ function Header({
<WeatherModal
current={current}
outlook={outlook}
hourly={hourly}
closeWeatherModal={closeWeatherModal}
weatherModalOpened={weatherModalOpened}
setProviderLabel={setProviderLabel}
setProviderURL={setProviderURL}
setValue={setValue}
isMobile={isMobile}
/>
</Stack>
);

View File

@@ -1,25 +1,26 @@
import { Stack, TextInput } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { HelpModal } from '../../../../components/HelpModal/HelpModal';
import { WeatherModal } from '../../../../components/WeatherModal/WeatherModal';
import { containsProtocol, useQueryAPI, validURL } from '../../../../hooks';
import { textInputClasses } from '../../../../styles';
import { HelpModal, WeatherModal } from 'components';
import { containsProtocol, useCommonHooks, validURL } from 'hooks';
import { textInputClasses } from 'styles';
export default function Search({
current,
outlook,
hourly,
weatherModalOpened,
closeWeatherModal,
openWeatherModal,
providerLabel,
setProviderLabel,
providerURL,
setProviderURL,
value,
setValue,
}: any) {
const { data: providers } = useQueryAPI({ endpoint: 'api/providers' });
const {
providerLabel,
setProviderLabel,
providerURL,
setProviderURL,
searchValue,
setSearchValue,
providers,
} = useCommonHooks();
const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
useDisclosure(false);
@@ -72,10 +73,10 @@ export default function Search({
classNames={textInputClasses}
spellCheck='true'
leftSection={providerLabel}
value={value}
data-active={value !== ''}
value={searchValue}
data-active={searchValue !== ''}
onChange={(item: any) => {
setValue(item.currentTarget.value);
setSearchValue(item.currentTarget.value);
if (
item.target.value === '/?' ||
item.target.value === '!help' ||
@@ -104,22 +105,14 @@ export default function Search({
}}
/>
<HelpModal
providers={providers}
providerModalOpened={providerModalOpened}
closeProviderModal={closeProviderModal}
setValue={setValue}
setProviderLabel={setProviderLabel}
setProviderURL={setProviderURL}
/>
<WeatherModal
current={current}
outlook={outlook}
hourly={hourly}
closeWeatherModal={closeWeatherModal}
weatherModalOpened={weatherModalOpened}
setValue={setValue}
setProviderLabel={setProviderLabel}
setProviderURL={setProviderURL}
/>
</Stack>
);

View File

@@ -1,19 +1,26 @@
import { Button, Menu, MultiSelect, NavLink } from '@mantine/core';
import { useQueryAPI } from 'hooks';
import { Button, Menu, MultiSelect, NavLink, Switch } from '@mantine/core';
import { runWakeCommand, useCommonHooks, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { FiMenu, FiMonitor } from 'react-icons/fi';
import { FiMenu, FiMonitor, FiShuffle } from 'react-icons/fi';
import { useNavigate } from 'react-router-dom';
import { buttonClasses, menuClasses, navLinkClasses, selectClasses } from 'styles';
function SettingsButton({
appFilter,
setAppFilter,
bookmarkFilter,
setBookmarkFilter,
}: any) {
function SettingsButton() {
const { data, isFetched } = useQueryAPI({ endpoint: 'api/appCategories' });
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);
const navigate = useNavigate();
const {
currentTheme,
setCurrentTheme,
handleThemeChange,
theme,
allThemes,
appFilter,
setAppFilter,
bookmarkFilter,
setBookmarkFilter,
} = useCommonHooks();
useEffect(() => {
if (data) {
const list: [any] =
@@ -25,13 +32,6 @@ function SettingsButton({
}
}, [data, isFetched]);
function runWakeCommand() {
const requestOptions = {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
};
fetch(`/${import.meta.env.VITE_BASE_URL}/api/wakeDakota`, requestOptions);
}
return (
<Menu withinPortal={false} withArrow shadow='md' position='top'>
<Menu.Target>
@@ -59,6 +59,31 @@ function SettingsButton({
className={navLinkClasses.defaultNavLink}
classNames={navLinkClasses}
/>
<NavLink
label='Shuffle Themes'
leftSection={
<Switch
labelPosition='left'
checked={currentTheme === 'random'}
onChange={(e) => {
setCurrentTheme(e.target.checked ? 'random' : theme.name);
}}
color={theme.primaryColor}
/>
}
onClick={() => {}}
className={navLinkClasses.defaultNavLink}
classNames={navLinkClasses}
/>
<NavLink
label='Random Theme'
leftSection={<FiShuffle />}
onClick={() => {
handleThemeChange(allThemes, true);
}}
className={navLinkClasses.defaultNavLink}
classNames={navLinkClasses}
/>
</NavLink>
<MultiSelect
label='App Filter'

View File

@@ -1,4 +1,5 @@
import { ActionIcon, Group, Stack, Tabs, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import {
FiArrowLeftCircle,
FiBook,
@@ -19,8 +20,8 @@ import Personalization from './components/Personalization';
import SearchProviders from './components/SearchProviders';
import Themes from './components/Themes';
export function Settings({ theme, setTheme, allThemes }: any) {
const isMobile = window.matchMedia('(max-width: 600px)').matches;
export function Settings() {
const { isMobile } = useCommonHooks();
const navigate = useNavigate();
return (
<Stack px={'2rem'}>
@@ -76,7 +77,7 @@ export function Settings({ theme, setTheme, allThemes }: any) {
<Categories />
</Tabs.Panel>
<Tabs.Panel value='themes'>
<Themes theme={theme} setTheme={setTheme} allThemes={allThemes} />
<Themes />
</Tabs.Panel>
<Tabs.Panel value='providers'>
<SearchProviders />

View File

@@ -1,21 +1,11 @@
import { Stack, Text } from '@mantine/core';
import { ApplicationColumns, DataTable } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useCommonHooks } from 'hooks';
import { textClasses } from 'styles';
const ApplicationSettings = () => {
const endpoint = 'api/apps';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint,
});
const [applicationData, setApplicationData] = useState<any>([]);
useEffect(() => {
if (isFetched) {
setApplicationData(data);
}
}, [data, isFetching]);
const { isAppsFetching, isAppsLoading, applicationData } = useCommonHooks();
return (
<Stack>
@@ -26,8 +16,8 @@ const ApplicationSettings = () => {
tableName='Applications'
columns={ApplicationColumns()}
tableData={applicationData}
isFetching={isFetching}
isLoading={isLoading}
isFetching={isAppsFetching}
isLoading={isAppsLoading}
endpoint={endpoint}
/>
</Stack>

View File

@@ -1,31 +1,11 @@
import { Stack, Text } from '@mantine/core';
import { BookmarkColumns, DataTable } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useCommonHooks } from 'hooks';
import { textClasses } from 'styles';
const BookmarkSettings = () => {
const endpoint = 'api/links';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint,
});
const { data: groupData } = useQueryAPI({ endpoint: 'api/categories' });
const [bookmarkData, setBookmarkData] = useState<any>([]);
const [groups, setGroups] = useState([]);
const { groups, bookmarkData, linksIsFetching, linksIsLoading } = useCommonHooks();
useEffect(() => {
if (isFetched) {
setBookmarkData(data);
}
});
useEffect(() => {
if (groupData) {
const groupList = groupData?.map((category: any) => {
return category.categoryName;
});
setGroups(groupList);
}
}, [groupData]);
return (
<Stack>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
@@ -35,8 +15,8 @@ const BookmarkSettings = () => {
tableName='Applications'
columns={BookmarkColumns(groups)}
tableData={bookmarkData}
isFetching={isFetching}
isLoading={isLoading}
isFetching={linksIsFetching}
isLoading={linksIsLoading}
endpoint={endpoint}
/>
</Stack>

View File

@@ -1,21 +1,10 @@
import { Stack, Text } from '@mantine/core';
import { CategoryColumns, DataTable } from 'components';
import { useEffect, useState } from 'react';
import { useQueryAPI } from '../../../hooks/useAPI';
import { useCommonHooks } from 'hooks';
import { textClasses } from '../../../styles';
const CategorySettings = () => {
const endpoint = 'api/categories';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint,
});
const [categoryData, setCategoryData] = useState<any>([]);
useEffect(() => {
if (data && isFetched) {
setCategoryData(data);
}
}, [data, isFetched]);
const { groupData, groupsIsFetching, groupsIsLoading } = useCommonHooks();
return (
<Stack>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
@@ -24,9 +13,9 @@ const CategorySettings = () => {
<DataTable
tableName='Categories'
columns={CategoryColumns()}
tableData={categoryData}
isFetching={isFetching}
isLoading={isLoading}
tableData={groupData ?? []}
isFetching={groupsIsFetching}
isLoading={groupsIsLoading}
endpoint={endpoint}
/>
</Stack>

View File

@@ -1,21 +1,10 @@
import { Stack, Text } from '@mantine/core';
import { DataTable, MessagesColumns } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useCommonHooks } from 'hooks';
import { textClasses } from 'styles';
const MessageSettings = () => {
const endpoint = 'api/messages';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint,
});
const [messageData, setMessageData] = useState<any>([]);
useEffect(() => {
if (isFetched) {
setMessageData(data);
}
});
const { messageData, isMessagesFetching, isMessagesLoading } = useCommonHooks();
return (
<Stack>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
@@ -25,8 +14,8 @@ const MessageSettings = () => {
tableName='Messages'
columns={MessagesColumns()}
tableData={messageData}
isFetching={isFetching}
isLoading={isLoading}
isFetching={isMessagesFetching}
isLoading={isMessagesLoading}
endpoint={endpoint}
/>
</Stack>

View File

@@ -1,20 +1,10 @@
import { Text } from '@mantine/core';
import { DataTable, PersonalizationColumns } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useCommonHooks } from 'hooks';
import { textClasses } from 'styles';
const PersonalizationSettings = () => {
const endpoint = 'api/personalData';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint,
});
const [personalData, setPersonalData] = useState<any>([]);
useEffect(() => {
if (isFetched) {
setPersonalData(data);
}
});
const { personalData, personalIsFetching, personalIsLoading } = useCommonHooks();
return (
<>
@@ -25,8 +15,8 @@ const PersonalizationSettings = () => {
tableName='Personalization'
columns={PersonalizationColumns()}
tableData={personalData}
isFetching={isFetching}
isLoading={isLoading}
isFetching={personalIsFetching}
isLoading={personalIsLoading}
endpoint={endpoint}
/>
</>

View File

@@ -1,21 +1,11 @@
import { Stack, Text } from '@mantine/core';
import { DataTable, SearchProviderColumns } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useCommonHooks } from 'hooks';
import { textClasses } from 'styles';
const SearchProviderSettings = () => {
const endpoint = 'api/providers';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint,
});
const [providerData, setProviderData] = useState<any>([]);
useEffect(() => {
if (isFetched) {
setProviderData(data);
}
});
const { providerData, providersIsFetching, providersIsLoading } = useCommonHooks();
return (
<Stack>
@@ -26,8 +16,8 @@ const SearchProviderSettings = () => {
tableName='Categories'
columns={SearchProviderColumns()}
tableData={providerData}
isFetching={isFetching}
isLoading={isLoading}
isFetching={providersIsFetching}
isLoading={providersIsLoading}
endpoint={endpoint}
/>
</Stack>

View File

@@ -1,21 +1,14 @@
import { ActionIcon, Box, Button, Group, Overlay, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { useState } from 'react';
import { FiEdit, FiShuffle, FiTrash, FiX } from 'react-icons/fi';
import { actionIconClasses } from 'styles';
function ThemeCard({
handleOpen,
handleDeleteTheme,
theme,
allThemes,
setTheme,
cardTheme,
currentTheme,
setValue,
createTheme,
}: any) {
function ThemeCard({ cardTheme, handleOpen, handleDeleteTheme, createTheme }: any) {
const [hover, setHover] = useState(false);
const [shift, setShift] = useState<number>(0);
const { handleThemeChange, currentTheme, setCurrentTheme, theme, allThemes } =
useCommonHooks();
return (
<Group
gap={0}
@@ -54,9 +47,8 @@ function ThemeCard({
backgroundColor: cardTheme.backgroundColor,
border: `solid ${cardTheme.borderColor}`,
}}
// data-cardTheme={cardTheme.name}
onClick={() => {
setValue(cardTheme.name);
setCurrentTheme(cardTheme.name);
}}
>
{hover && currentTheme !== 'random' && <Overlay opacity={0.5} zIndex={0} />}
@@ -174,15 +166,7 @@ function ThemeCard({
>
<FiShuffle
onClick={() => {
const enabled = allThemes
.filter((t: any) => t.randomEnabled)
.filter((t: any) => t.name !== theme.name);
if (enabled.length === 0) {
setTheme(allThemes[0]);
} else {
const index = Math.floor(Math.random() * enabled.length);
setTheme(enabled[index]);
}
handleThemeChange(allThemes, false);
}}
style={{
fontSize: '2em',

View File

@@ -13,6 +13,7 @@ import {
Text,
TextInput,
} from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { FiSave, FiX } from 'react-icons/fi';
import { modalClasses, selectClasses, textClasses } from 'styles';
import ThemeModalContent from './ThemeModalContent';
@@ -22,7 +23,6 @@ function ThemeModal({
handleClose,
selectedTheme,
setSelectedTheme,
theme,
setColorId,
colorId,
setColorValue,
@@ -33,6 +33,7 @@ function ThemeModal({
closePicker,
handleSaveTheme,
}: any) {
const { theme } = useCommonHooks();
return (
<Modal.Root
size={'50%'}

View File

@@ -1,6 +1,6 @@
import { Box, Button, Group, Overlay, Stack, Switch, Text } from '@mantine/core';
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { useMutationAPI, useQueryAPI } from 'hooks';
import { useDisclosure } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { FiChevronRight, FiPlus } from 'react-icons/fi';
import '@mantine/core/styles/ModalBase.css';
@@ -9,23 +9,26 @@ import { modals } from '@mantine/modals';
import { buttonClasses, textClasses } from 'styles';
import ThemeCard from './ThemeCard';
import ThemeModal from './ThemeModal';
function ThemesSettings({ theme, setTheme, allThemes }: any) {
function ThemesSettings() {
const [opened, { open, close }] = useDisclosure(false);
const [pickerOpen, { open: openPicker, close: closePicker }] = useDisclosure(false);
const [colorId, setColorId] = useState<string>('');
const [colorValue, setColorValue] = useState('#000000');
const [currentTheme, setValue] = useLocalStorage<any>({
key: 'theme',
defaultValue: 'blackboard',
});
const { data: backgrounds, isFetched: isBackgroundFetched } = useQueryAPI({
endpoint: 'api/backgrounds',
});
const [backgroundList, setBackgroundList] = useState<any>(['None']);
const [selectedTheme, setSelectedTheme] = useState<any>({});
const { mutateAsync: createTheme } = useMutationAPI();
const { mutateAsync: deleteTheme } = useMutationAPI();
const isMobile = window.matchMedia('(max-width: 600px)').matches;
const {
currentTheme,
setCurrentTheme,
backgrounds,
isBackgroundFetched,
isMobile,
theme,
allThemes,
} = useCommonHooks();
useEffect(() => {
if (backgrounds && isBackgroundFetched) {
@@ -114,7 +117,7 @@ function ThemesSettings({ theme, setTheme, allThemes }: any) {
labelPosition='left'
checked={currentTheme === 'random'}
onChange={(e) => {
setValue(e.target.checked ? 'random' : theme.name);
setCurrentTheme(e.target.checked ? 'random' : theme.name);
}}
color={theme.primaryColor}
/>
@@ -158,14 +161,9 @@ function ThemesSettings({ theme, setTheme, allThemes }: any) {
}}
>
<ThemeCard
theme={theme}
allThemes={allThemes}
setTheme={setTheme}
cardTheme={cardTheme}
currentTheme={currentTheme}
handleOpen={handleOpen}
handleDeleteTheme={handleDeleteTheme}
setValue={setValue}
createTheme={createTheme}
/>
</Box>