This commit is contained in:
Sean Edmunds
2024-12-06 15:33:58 -06:00
parent 00c141d8d7
commit 33813a34fb
48 changed files with 2188 additions and 420 deletions

View File

@@ -4,5 +4,3 @@
- Create actions section - Create actions section
- Fix all query/setState problems - Fix all query/setState problems
- Random "feeling lucky" or subreddit button

1934
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,12 @@
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.9", "eslint-plugin-react-refresh": "^0.4.9",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"vite": "^5.4.0" "vite": "^5.4.0",
"vite-plugin-dts": "~4.0.1",
"vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.0.5",
"@vitest/coverage-v8": "^2.0.5",
"@vitest/ui": "^2.0.5"
} }
} }

View File

@@ -7,6 +7,7 @@ import {
} from '@mantine/core'; } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
import { ModalsProvider } from '@mantine/modals'; import { ModalsProvider } from '@mantine/modals';
import { useQueryAPI } from 'hooks';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { import {
Route, Route,
@@ -14,24 +15,22 @@ import {
createBrowserRouter, createBrowserRouter,
createRoutesFromElements, createRoutesFromElements,
} from 'react-router-dom'; } from 'react-router-dom';
import ThemeProvider from './components/ThemeProvider'; import { ThemeProvider } from './components';
import { useQueryAPI } from './hooks/useAPI';
import Homepage from './pages/homepage/Homepage';
import Settings from './pages/settings/Settings';
import '@mantine/core/styles.css'; //import Mantine V7 styles needed by MRT 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/dates/styles.css'; //if using mantine date picker features
import 'mantine-react-table/styles.css'; //import MRT styles import 'mantine-react-table/styles.css'; //import MRT styles
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { appTheme } from 'contexts';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { appTheme } from './contexts/theme';
import './index.css'; import './index.css';
import { Homepage, Settings } from 'pages';
function App() { function App() {
const { data, isFetched } = useQueryAPI({ const { data, isFetched } = useQueryAPI({
endpoint: 'api/themes', endpoint: 'api/themes',
}); });
const [themeName, setThemeName] = useLocalStorage<any>({ const [themeName, _] = useLocalStorage<any>({
key: 'theme', key: 'theme',
defaultValue: 'blackboard', defaultValue: 'blackboard',
}); });

View File

@@ -5,15 +5,15 @@ import {
useMantineReactTable, useMantineReactTable,
} from 'mantine-react-table'; } from 'mantine-react-table';
import { memo } from 'react'; import { memo } from 'react';
import '../dataTableStyles.css'; import '../../../src/dataTableStyles.css';
import { ActionIcon, Button, Flex, Text } from '@mantine/core'; import { ActionIcon, Button, Flex, Text } from '@mantine/core';
import { modals } from '@mantine/modals'; import { modals } from '@mantine/modals';
import { IconEdit, IconTrash } from '@tabler/icons-react'; import { IconEdit, IconTrash } from '@tabler/icons-react';
import { appTheme } from 'contexts';
import { useMutationAPI } from 'hooks';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { FiPlus } from 'react-icons/fi'; import { FiPlus } from 'react-icons/fi';
import { appTheme } from '../contexts/theme'; import { actionIconClasses, buttonClasses } from 'styles';
import { useMutationAPI } from '../hooks/useAPI';
import { actionIconClasses, buttonClasses } from '../styles';
export function DataTableComponent({ export function DataTableComponent({
tableName, tableName,
@@ -22,7 +22,6 @@ export function DataTableComponent({
isFetching, isFetching,
isLoading, isLoading,
endpoint, endpoint,
grouping,
}: { }: {
tableName: string; tableName: string;
columns: any; columns: any;
@@ -30,9 +29,8 @@ export function DataTableComponent({
isFetching: boolean; isFetching: boolean;
isLoading: boolean; isLoading: boolean;
endpoint: string; endpoint: string;
grouping?: string;
}) { }) {
const [theme, setTheme] = useAtom(appTheme); const [theme, _] = useAtom(appTheme);
const { mutateAsync: createRow } = useMutationAPI(); const { mutateAsync: createRow } = useMutationAPI();
const { mutateAsync: editRow } = useMutationAPI(); const { mutateAsync: editRow } = useMutationAPI();
const { mutateAsync: deleteRow } = useMutationAPI(); const { mutateAsync: deleteRow } = useMutationAPI();

View File

@@ -1,7 +1,7 @@
import type { MRT_ColumnDef } from 'mantine-react-table'; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from 'react'; import { useMemo } from 'react';
export default function ApplicationColumns() { export function ApplicationColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
() => [ () => [
{ {
@@ -15,7 +15,7 @@ export default function ApplicationColumns() {
id: 'id', id: 'id',
header: 'ID', header: 'ID',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -27,7 +27,7 @@ export default function ApplicationColumns() {
}, },
id: 'name', id: 'name',
header: 'Name', header: 'Name',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -39,7 +39,7 @@ export default function ApplicationColumns() {
}, },
id: 'url', id: 'url',
header: 'Url', header: 'Url',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -51,7 +51,7 @@ export default function ApplicationColumns() {
}, },
id: 'icon', id: 'icon',
header: 'Icon', header: 'Icon',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -63,7 +63,7 @@ export default function ApplicationColumns() {
}, },
id: 'baseUrl', id: 'baseUrl',
header: 'Base Url', header: 'Base Url',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -75,7 +75,7 @@ export default function ApplicationColumns() {
}, },
id: 'filterName', id: 'filterName',
header: 'Filter Name', header: 'Filter Name',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
], ],

View File

@@ -1,7 +1,7 @@
import type { MRT_ColumnDef } from 'mantine-react-table'; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from 'react'; import { useMemo } from 'react';
export default function ApplicationColumns(groups: any) { export function BookmarkColumns(groups: any) {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
() => [ () => [
{ {
@@ -15,7 +15,7 @@ export default function ApplicationColumns(groups: any) {
id: 'id', id: 'id',
header: 'ID', header: 'ID',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -27,7 +27,7 @@ export default function ApplicationColumns(groups: any) {
}, },
id: 'name', id: 'name',
header: 'Name', header: 'Name',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -39,7 +39,7 @@ export default function ApplicationColumns(groups: any) {
}, },
id: 'url', id: 'url',
header: 'Url', header: 'Url',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {

View File

@@ -1,7 +1,7 @@
import type { MRT_ColumnDef } from 'mantine-react-table'; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from 'react'; import { useMemo } from 'react';
export default function CategoryColumns() { export function CategoryColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
() => [ () => [
{ {
@@ -15,7 +15,7 @@ export default function CategoryColumns() {
id: 'id', id: 'id',
header: 'ID', header: 'ID',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -28,7 +28,7 @@ export default function CategoryColumns() {
id: 'categoryName', id: 'categoryName',
header: 'Name', header: 'Name',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -42,7 +42,7 @@ export default function CategoryColumns() {
id: 'categoryIndex', id: 'categoryIndex',
header: 'Index', header: 'Index',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -55,7 +55,7 @@ export default function CategoryColumns() {
id: 'filterName', id: 'filterName',
header: 'Filter Name', header: 'Filter Name',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
], ],

View File

@@ -1,7 +1,7 @@
import type { MRT_ColumnDef } from 'mantine-react-table'; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from 'react'; import { useMemo } from 'react';
export default function MessagesColumns() { export function MessagesColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
() => [ () => [
{ {
@@ -15,7 +15,7 @@ export default function MessagesColumns() {
id: 'id', id: 'id',
header: 'ID', header: 'ID',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -27,7 +27,7 @@ export default function MessagesColumns() {
}, },
id: 'message', id: 'message',
header: 'Message', header: 'Message',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
], ],

View File

@@ -1,7 +1,7 @@
import type { MRT_ColumnDef } from 'mantine-react-table'; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from 'react'; import { useMemo } from 'react';
export default function PersonalizationColumns() { export function PersonalizationColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
() => [ () => [
{ {
@@ -14,7 +14,7 @@ export default function PersonalizationColumns() {
id: 'firstName', id: 'firstName',
header: 'First Name', header: 'First Name',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -26,7 +26,7 @@ export default function PersonalizationColumns() {
}, },
id: 'lastName', id: 'lastName',
header: 'Last Name', header: 'Last Name',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -38,7 +38,7 @@ export default function PersonalizationColumns() {
}, },
id: 'email', id: 'email',
header: 'Email', header: 'Email',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
], ],

View File

@@ -1,7 +1,7 @@
import type { MRT_ColumnDef } from 'mantine-react-table'; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from 'react'; import { useMemo } from 'react';
export default function SearchProviderColumns() { export function SearchProviderColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
() => [ () => [
{ {
@@ -15,7 +15,7 @@ export default function SearchProviderColumns() {
id: 'id', id: 'id',
header: 'ID', header: 'ID',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -27,7 +27,7 @@ export default function SearchProviderColumns() {
}, },
id: 'name', id: 'name',
header: 'Name', header: 'Name',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -39,7 +39,7 @@ export default function SearchProviderColumns() {
}, },
id: 'url', id: 'url',
header: 'Url', header: 'Url',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
{ {
@@ -51,7 +51,7 @@ export default function SearchProviderColumns() {
}, },
id: 'prefix', id: 'prefix',
header: 'Prefix', header: 'Prefix',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
], ],

View File

@@ -1,9 +1,9 @@
import { Divider, Group, Modal, Stack, Text } from '@mantine/core'; import { Divider, Group, Modal, Stack, Text } from '@mantine/core';
import { appTheme } from 'contexts';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { appTheme } from '../../../contexts/theme'; import { modalClasses } from 'styles';
import { modalClasses } from '../../../styles';
export default function HelpModal({ export function HelpModal({
setValue, setValue,
setProviderLabel, setProviderLabel,
setProviderURL, setProviderURL,
@@ -11,7 +11,7 @@ export default function HelpModal({
providerModalOpened, providerModalOpened,
closeProviderModal, closeProviderModal,
}: any) { }: any) {
const [theme, setTheme] = useAtom(appTheme); const [theme, _] = useAtom(appTheme);
return ( return (
<Modal.Root <Modal.Root
size={'60%'} size={'60%'}

View File

@@ -1,6 +1,6 @@
import { Box } from '@mantine/core'; import { Box } from '@mantine/core';
export default function ThemeProvider({ export function ThemeProvider({
theme, theme,
children, children,
}: { theme: any; children: React.ReactNode }) { }: { theme: any; children: React.ReactNode }) {

View File

@@ -1,5 +1,5 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { stackClasses } from '../styles'; import { stackClasses } from 'styles';
export function ToastError({ err }: { err: any }) { export function ToastError({ err }: { err: any }) {
const errorResponse = JSON.parse(err.message); const errorResponse = JSON.parse(err.message);

View File

@@ -112,7 +112,7 @@ export default function HourlyCard({ weather }: any) {
transform: `rotate(${WindDirection[weather.windDirection]}deg)`, transform: `rotate(${WindDirection[weather.windDirection]}deg)`,
}} }}
/> />
{`${weather.probabilityOfPrecipitation.value * 10}`}% {`${weather.probabilityOfPrecipitation.value}`}%
</Group> </Group>
</Text> </Text>
</Stack> </Stack>

View File

@@ -1,14 +1,14 @@
import { Center, Divider, Group, Modal, Stack } from '@mantine/core'; import { Center, Divider, Group, Modal, Stack } from '@mantine/core';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { modalClasses } from '../../../styles'; import { modalClasses } from 'styles';
import SmallOutlookCard from './SmallOutlookCard'; import SmallOutlookCard from './SmallOutlookCard';
import SmallWeatherCard from './SmallWeatherCard'; import SmallWeatherCard from './SmallWeatherCard';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { appTheme } from '../../../contexts/theme'; import { appTheme } from '../../contexts/theme';
import HourlyCard from './HourlyCard'; import HourlyCard from './HourlyCard';
export default function WeatherModal({ export function WeatherModal({
current, current,
outlook, outlook,
hourly, hourly,
@@ -18,7 +18,7 @@ export default function WeatherModal({
setProviderLabel, setProviderLabel,
setProviderURL, setProviderURL,
}: any) { }: any) {
const [theme, setTheme] = useAtom(appTheme); const [theme, _] = useAtom(appTheme);
const [hourlyForecast, setHourlyForecast] = useState<any>([]); const [hourlyForecast, setHourlyForecast] = useState<any>([]);
const [sunset, setSunset] = useState(100); const [sunset, setSunset] = useState(100);
@@ -30,8 +30,13 @@ export default function WeatherModal({
useEffect(() => { useEffect(() => {
if (hourlyForecast) { if (hourlyForecast) {
setSunset( hourlyForecast[0]?.isDaytime
(hourlyForecast.findIndex((item: any) => item.isDaytime === false) / 12) * 100, ? setSunset(
(hourlyForecast.findIndex((item: any) => item.isDaytime === false) / 12) *
100,
)
: setSunset(
(hourlyForecast.findIndex((item: any) => item.isDaytime === true) / 12) * 100,
); );
} }
}, [hourlyForecast]); }, [hourlyForecast]);

10
src/components/index.ts Normal file
View File

@@ -0,0 +1,10 @@
export { HelpModal } from './HelpModal/HelpModal';
export { WeatherModal } from './WeatherModal/WeatherModal';
export { DataTable } from './DataTable/DataTable';
export { ThemeProvider } from './ThemeProvider';
export { ApplicationColumns } from './DataTable/columns/ApplicationColumns';
export { BookmarkColumns } from './DataTable/columns/BookmarkColumns';
export { CategoryColumns } from './DataTable/columns/CategoryColumns';
export { MessagesColumns } from './DataTable/columns/MessagesColumns';
export { PersonalizationColumns } from './DataTable/columns/PersonalizationColumns';
export { SearchProviderColumns } from './DataTable/columns/SearchProviderColumns';

2
src/contexts/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export { HomepageFilters } from './HomepageFilters';
export { appTheme } from './theme';

47
src/hooks/helpers.ts Normal file
View File

@@ -0,0 +1,47 @@
export function date(isMobile: boolean) {
const currentDate = new Date();
const dateOptions: Intl.DateTimeFormatOptions = !isMobile
? {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
}
: {};
const date = currentDate.toLocaleDateString('en-US', dateOptions);
return date;
}
export function greet(list: any) {
const currentTime = new Date();
const greet = Math.floor(currentTime.getHours() / 6);
const easterEggChance = Math.floor(Math.random() * 100);
let timeBased: any = [];
let customGreetings: any = [];
for (const l of list) {
if (l.customGreeting === 0) {
timeBased = timeBased.concat(l);
} else {
customGreetings = customGreetings.concat(l);
}
}
if (easterEggChance > 10 || customGreetings.length === 0) {
return timeBased[greet].message;
}
return customGreetings[Math.floor(Math.random() * customGreetings.length)].message;
}
export function validURL(str: any) {
const pattern = new RegExp(
'^(https?:\\/\\/)?' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
'(\\#[-a-z\\d_]*)?$',
'i',
); // fragment locator
return !!pattern.test(str);
}
export function containsProtocol(str: any) {
const pattern = /^(https?:\/\/).*/i;
return !!pattern.test(str);
}

View File

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

View File

@@ -1,9 +1,4 @@
import { import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
useInfiniteQuery,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@@ -48,19 +43,7 @@ export function useWeatherAPI({ endpoint }: { endpoint: string }) {
} else if (endpoint === 'hourly') { } else if (endpoint === 'hourly') {
url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast/hourly'; url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast/hourly';
} }
const callEndpoint = async (endpoint: string) => { const callEndpoint = async () => {
const options: object = {
method: 'GET',
headers: {
'Content-Type': 'application/geo+json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
},
};
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
throw new Error(`${response.status} ${response.statusText}`); throw new Error(`${response.status} ${response.statusText}`);
@@ -70,7 +53,7 @@ export function useWeatherAPI({ endpoint }: { endpoint: string }) {
return useQuery({ return useQuery({
queryKey: ['GET', url], queryKey: ['GET', url],
queryFn: () => callEndpoint(endpoint), queryFn: () => callEndpoint(),
}); });
} }
@@ -343,8 +326,7 @@ interface DownloadFileOptions {
} }
export function useDownloadFile() { export function useDownloadFile() {
const downloadFile = useCallback( const downloadFile = useCallback(async ({ file_name }: DownloadFileOptions) => {
async ({ endpoint, file_name }: DownloadFileOptions) => {
const options: any = { const options: any = {
method: 'GET', method: 'GET',
headers: { headers: {
@@ -408,64 +390,7 @@ export function useDownloadFile() {
closeButton: null, closeButton: null,
}); });
} }
}, }, []);
[],
);
return downloadFile; return downloadFile;
} }
export const useFetchInfiniteQuery = ({
endpoint,
pageSize = 10,
}: {
endpoint: string;
pageSize?: number;
}) => {
const queryKey = ['GET', endpoint];
const fetchInfiniteQuery = async (endpoint: string) => {
const requestOptions: any = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
},
};
try {
const response = await fetch(url, requestOptions);
return await response.json();
} catch (error) {
console.error('Error fetching transactions:', error);
throw error;
}
};
const queryFn = ({ pageParam = 1 }) =>
fetchInfiniteQuery(`${endpoint}&page=${pageParam}&size=${pageSize}`);
const getNextPageParam = (lastPage: any, allPages: any) => {
if (lastPage.items.length < pageSize) {
return undefined;
}
return allPages.length + 1;
};
const { data, isLoading, isFetchingNextPage, fetchNextPage, isError, isFetching } =
useInfiniteQuery({
queryKey,
queryFn,
getNextPageParam,
initialPageParam: 1,
});
return {
data,
isLoading,
isFetchingNextPage,
fetchNextPage,
isError,
isFetching,
};
};

View File

@@ -1,18 +1,16 @@
import { Stack } from '@mantine/core'; import { Stack } from '@mantine/core';
import { useDisclosure, useLocalStorage } from '@mantine/hooks'; import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { date, greet, useQueryAPI, useWeatherAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useQueryAPI, useWeatherAPI } from '../../hooks/useAPI'; import { stackClasses } from 'styles';
import stackClasses from '../../styles/Stack.module.css'; import type { CurrentWeather, OutlookWeather } from 'types';
import Applications from './components/Applications'; import Applications from './components/Applications/Applications';
import Bookmarks from './components/Bookmarks'; import Bookmarks from './components/Bookmarks/Bookmarks';
import Header from './components/Header'; import Header from './components/Header/Header';
import Search from './components/Search'; import Search from './components/Search/Search';
import SettingsButton from './components/SettingsButton'; import SettingsButton from './components/SettingsButton';
import type { CurrentWeather, OutlookWeather } from './types/weather';
export default function Homepage({ theme }: any) { export function Homepage({ theme }: any) {
const { data, isFetching } = useQueryAPI({ endpoint: 'api/messages' });
const [displayedGreeting, setDisplayedGreeting] = useState('');
const [appFilter, setAppFilter] = useLocalStorage<[any]>({ const [appFilter, setAppFilter] = useLocalStorage<[any]>({
key: 'appFilter', key: 'appFilter',
defaultValue: ['All'], defaultValue: ['All'],
@@ -21,8 +19,12 @@ export default function Homepage({ theme }: any) {
key: 'bookmarkFilter', key: 'bookmarkFilter',
defaultValue: ['All'], defaultValue: ['All'],
}); });
const { data, isFetching } = useQueryAPI({ endpoint: 'api/messages' });
const { data: basicWeather } = useWeatherAPI({ endpoint: 'baseline' }); const { data: basicWeather } = useWeatherAPI({ endpoint: 'baseline' });
const { data: weather } = useWeatherAPI({ endpoint: 'current' }); const { data: weather } = useWeatherAPI({ endpoint: 'current' });
const { data: forecast } = useWeatherAPI({ endpoint: 'forecast' });
const { data: hourly } = useWeatherAPI({ endpoint: 'hourly' });
const [displayedGreeting, setDisplayedGreeting] = useState('');
const [current, setCurrent] = useState<CurrentWeather>({ const [current, setCurrent] = useState<CurrentWeather>({
icon: '', icon: '',
temperature: 0, temperature: 0,
@@ -45,42 +47,9 @@ export default function Homepage({ theme }: any) {
windDirection: 'N', windDirection: 'N',
}, },
]); ]);
const { data: forecast } = useWeatherAPI({ endpoint: 'forecast' }); const isMobile = window.matchMedia('(max-width: 600px)').matches;
const { data: hourly } = useWeatherAPI({ endpoint: 'hourly' });
const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] = const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
useDisclosure(false); useDisclosure(false);
const isMobile = window.matchMedia('(max-width: 600px)').matches;
function date() {
const currentDate = new Date();
const dateOptions: Intl.DateTimeFormatOptions = !isMobile
? {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
}
: {};
const date = currentDate.toLocaleDateString('en-US', dateOptions);
return date;
}
function greet(list: any) {
const currentTime = new Date();
const greet = Math.floor(currentTime.getHours() / 6);
const easterEggChance = Math.floor(Math.random() * 100);
let timeBased: any = [];
let customGreetings: any = [];
for (const l of list) {
if (l.customGreeting === 0) {
timeBased = timeBased.concat(l);
} else {
customGreetings = customGreetings.concat(l);
}
}
if (easterEggChance > 10 || customGreetings.length === 0) {
return timeBased[greet].message;
}
return customGreetings[Math.floor(Math.random() * customGreetings.length)].message;
}
useEffect(() => { useEffect(() => {
if (data) { if (data) {
@@ -160,12 +129,12 @@ export default function Homepage({ theme }: any) {
openWeatherModal={openWeatherModal} openWeatherModal={openWeatherModal}
/> />
<Header <Header
date={date()} date={date(isMobile)}
greet={displayedGreeting} greet={displayedGreeting}
isMobile={isMobile} isMobile={isMobile}
current={current} current={current}
outlook={outlook}
setCurrent={setCurrent} setCurrent={setCurrent}
outlook={outlook}
weather={weather} weather={weather}
basicWeather={basicWeather} basicWeather={basicWeather}
forecast={forecast} forecast={forecast}

View File

@@ -1,8 +1,7 @@
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { Box, Stack, Text } from '@mantine/core'; import { Box, Stack, Text } from '@mantine/core';
import { useState } from 'react'; import { useState } from 'react';
import stacks from '../../../styles/Stack.module.css'; import { stackClasses, textClasses } from 'styles';
import texts from '../../../styles/Text.module.css';
function AppItem({ url, icon, name, isMobile }: any) { function AppItem({ url, icon, name, isMobile }: any) {
const [textDecoration, setTextDecoration] = useState<string>('none'); const [textDecoration, setTextDecoration] = useState<string>('none');
@@ -13,7 +12,7 @@ function AppItem({ url, icon, name, isMobile }: any) {
href={`https://${url}`} href={`https://${url}`}
onMouseOver={() => setTextDecoration('underline')} onMouseOver={() => setTextDecoration('underline')}
onMouseOut={() => setTextDecoration('none')} onMouseOut={() => setTextDecoration('none')}
className={stacks.appItemBox} className={stackClasses.appItemBox}
> >
<Stack <Stack
align={isMobile && 'center'} align={isMobile && 'center'}
@@ -38,12 +37,12 @@ function AppItem({ url, icon, name, isMobile }: any) {
gap: 0, gap: 0,
}} }}
> >
<Text className={texts.appName} classNames={texts}> <Text className={textClasses.appName} classNames={textClasses}>
{name} {name}
</Text> </Text>
<Text <Text
className={texts.appUrl} className={textClasses.appUrl}
classNames={texts} classNames={textClasses}
style={{ style={{
textDecoration: textDecoration, textDecoration: textDecoration,
}} }}

View File

@@ -1,7 +1,7 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useQueryAPI } from '../../../hooks/useAPI'; import { textClasses } from 'styles';
import texts from '../../../styles/Text.module.css';
import AppItem from './AppItem'; import AppItem from './AppItem';
function Applications({ contentType, isMobile }: any) { function Applications({ contentType, isMobile }: any) {
@@ -16,7 +16,7 @@ function Applications({ contentType, isMobile }: any) {
<> <>
{applications.length > 0 && contentType.length > 0 && ( {applications.length > 0 && contentType.length > 0 && (
<Stack id='apps' style={{ gap: 0 }}> <Stack id='apps' style={{ gap: 0 }}>
<Text className={texts.sectionTitle} classNames={texts}> <Text className={textClasses.sectionTitle} classNames={textClasses}>
Applications Applications
</Text> </Text>
<Stack <Stack

View File

@@ -1,7 +1,7 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useQueryAPI } from '../../../hooks/useAPI'; import { textClasses } from 'styles';
import texts from '../../../styles/Text.module.css';
import LinkGroups from './LinkGroups'; import LinkGroups from './LinkGroups';
function Links({ contentType, isMobile }: any) { function Links({ contentType, isMobile }: any) {
@@ -27,7 +27,7 @@ function Links({ contentType, isMobile }: any) {
categories?.length > 0 && categories?.length > 0 &&
contentType.length > 0 && ( contentType.length > 0 && (
<Stack id='links' style={{ gap: 0 }}> <Stack id='links' style={{ gap: 0 }}>
<Text className={texts.sectionTitle} classNames={texts}> <Text className={textClasses.sectionTitle} classNames={textClasses}>
Bookmarks Bookmarks
</Text> </Text>
<Stack <Stack

View File

@@ -1,5 +1,5 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import texts from '../../../styles/Text.module.css'; import { textClasses } from 'styles';
import LinkItem from './LinkItem'; import LinkItem from './LinkItem';
function LinkGroups({ links, category, isMobile }: any) { function LinkGroups({ links, category, isMobile }: any) {
@@ -12,7 +12,7 @@ function LinkGroups({ links, category, isMobile }: any) {
overflow: 'hidden', overflow: 'hidden',
}} }}
> >
<Text className={texts.linkCategory} classNames={texts}> <Text className={textClasses.linkCategory} classNames={textClasses}>
{category} {category}
</Text> </Text>
{links?.map((link: any) => ( {links?.map((link: any) => (

View File

@@ -1,11 +1,11 @@
import { Text } from '@mantine/core'; import { Text } from '@mantine/core';
import texts from '../../../styles/Text.module.css'; import { textClasses } from 'styles';
function LinkItem({ link, isMobile }: any) { function LinkItem({ link }: any) {
return ( return (
<Text <Text
className={texts.linkItem} className={textClasses.linkItem}
classNames={texts} classNames={textClasses}
component='a' component='a'
href={link.url} href={link.url}
style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}

View File

@@ -1,7 +1,7 @@
import { Group, Stack, Text } from '@mantine/core'; import { Group, Stack, Text } from '@mantine/core';
import { WeatherModal } from 'components';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { textClasses } from '../../../styles'; import { textClasses } from 'styles';
import WeatherModal from './WeatherModal';
function Header({ function Header({
greet, greet,

View File

@@ -1,34 +0,0 @@
import { useEffect, useState } from 'react';
import Messages from '../../../components/Messages';
import useQueryAPI from '../../../hooks/useAPI';
function MessageInfo() {
const messageData = useQueryAPI({ endpoint: 'api/messages' });
const [greetings, setGreetings] = useState([]);
useEffect(() => {
if (!messageData.isFetching) {
setGreetings(messageData.data);
}
}, [messageData.isFetching]);
return (
<>
{greetings && (
<div id='messageForm'>
{greetings.map((greeting, index) => (
<Messages
key={greeting.id}
group={index + 1}
index={greeting.index}
greeting={greeting.message}
easterEgg={greeting.customGreeting}
/>
))}
</div>
)}
</>
);
}
export default MessageInfo;

View File

@@ -1,12 +0,0 @@
function Provider(props) {
return (
<tr>
<td>
<a href={props.provider.url}>{props.provider.name}</a>
</td>
<td>{props.provider.prefix}</td>
</tr>
);
}
export default Provider;

View File

@@ -1,10 +1,10 @@
import { Stack, TextInput } from '@mantine/core'; import { Stack, TextInput } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from '@mantine/hooks';
import { useState } from 'react'; import { useState } from 'react';
import { useQueryAPI } from '../../../hooks'; import { HelpModal } from '../../../../components/HelpModal/HelpModal';
import { textInputClasses } from '../../../styles'; import { WeatherModal } from '../../../../components/WeatherModal/WeatherModal';
import HelpModal from './HelpModal'; import { containsProtocol, useQueryAPI, validURL } from '../../../../hooks';
import WeatherModal from './WeatherModal'; import { textInputClasses } from '../../../../styles';
export default function Search({ export default function Search({
current, current,
@@ -52,23 +52,6 @@ export default function Search({
} }
}; };
function validURL(str: any) {
const pattern = new RegExp(
'^(https?:\\/\\/)?' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
'(\\#[-a-z\\d_]*)?$',
'i',
); // fragment locator
return !!pattern.test(str);
}
function containsProtocol(str: any) {
const pattern = /^(https?:\/\/).*/i;
return !!pattern.test(str);
}
return ( return (
<Stack <Stack
style={{ style={{

View File

@@ -1,18 +1,14 @@
import { Button, Menu, MultiSelect, NavLink } from '@mantine/core'; import { Button, Menu, MultiSelect, NavLink } from '@mantine/core';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiMenu, FiMonitor } from 'react-icons/fi'; import { FiMenu, FiMonitor } from 'react-icons/fi';
import { useQueryAPI } from '../../../hooks/useAPI'; import { buttonClasses, menuClasses, navLinkClasses, selectClasses } from 'styles';
import button from '../../../styles/Button.module.css';
import menu from '../../../styles/Menu.module.css';
import navLink from '../../../styles/NavLink.module.css';
import selectClasses from '../../../styles/Select.module.css';
function SettingsButton({ function SettingsButton({
appFilter, appFilter,
setAppFilter, setAppFilter,
bookmarkFilter, bookmarkFilter,
setBookmarkFilter, setBookmarkFilter,
theme,
}: any) { }: any) {
const { data, isFetched } = useQueryAPI({ endpoint: 'api/appCategories' }); const { data, isFetched } = useQueryAPI({ endpoint: 'api/appCategories' });
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]); const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);
@@ -38,29 +34,29 @@ function SettingsButton({
return ( return (
<Menu withinPortal={false} withArrow shadow='md' position='top'> <Menu withinPortal={false} withArrow shadow='md' position='top'>
<Menu.Target> <Menu.Target>
<Button className={button.settingsButton} classNames={button}> <Button className={buttonClasses.settingsButton} classNames={buttonClasses}>
<FiMenu /> <FiMenu />
</Button> </Button>
</Menu.Target> </Menu.Target>
<Menu.Dropdown className={menu.dropdown}> <Menu.Dropdown className={menuClasses.dropdown}>
<NavLink <NavLink
label='Settings' label='Settings'
component='a' component='a'
href={`/${import.meta.env.VITE_BASE_URL}/settings`} href={`/${import.meta.env.VITE_BASE_URL}/settings`}
className={navLink.defaultNavLink} className={navLinkClasses.defaultNavLink}
classNames={navLink} classNames={navLinkClasses}
/> />
<NavLink <NavLink
label='Quick Actions' label='Quick Actions'
className={navLink.defaultNavLink} className={navLinkClasses.defaultNavLink}
classNames={navLink} classNames={navLinkClasses}
> >
<NavLink <NavLink
label='Wake Dakota' label='Wake Dakota'
leftSection={<FiMonitor />} leftSection={<FiMonitor />}
onClick={() => runWakeCommand()} onClick={() => runWakeCommand()}
className={navLink.defaultNavLink} className={navLinkClasses.defaultNavLink}
classNames={navLink} classNames={navLinkClasses}
/> />
</NavLink> </NavLink>
<MultiSelect <MultiSelect

2
src/pages/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export { Homepage } from './homepage/Homepage';
export { Settings } from './settings/Settings';

View File

@@ -1,7 +1,6 @@
import { ActionIcon, Group, Stack, Tabs, Text } from '@mantine/core'; import { ActionIcon, Group, Stack, Tabs, Text } from '@mantine/core';
import { FiArrowLeftCircle } from 'react-icons/fi'; import { FiArrowLeftCircle } from 'react-icons/fi';
import { actionIconClasses, textClasses } from '../../styles'; import { actionIconClasses, tabClasses, textClasses } from 'styles';
import tabClasses from '../../styles/Tabs.module.css';
import Applications from './components/Applications'; import Applications from './components/Applications';
import Bookmarks from './components/Bookmarks'; import Bookmarks from './components/Bookmarks';
import Categories from './components/Categories'; import Categories from './components/Categories';
@@ -10,7 +9,7 @@ import Personalization from './components/Personalization';
import SearchProviders from './components/SearchProviders'; import SearchProviders from './components/SearchProviders';
import Themes from './components/Themes'; import Themes from './components/Themes';
function Settings({ theme }: any) { export function Settings({ theme }: any) {
return ( return (
<Stack h={'100vh'} px={'2rem'}> <Stack h={'100vh'} px={'2rem'}>
<Group gap={0}> <Group gap={0}>

View File

@@ -1,9 +1,8 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { ApplicationColumns, DataTable } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { DataTable } from '../../../DataTable/DataTable'; import { textClasses } from 'styles';
import ApplicationColumns from '../../../DataTable/columns/ApplicationColumns';
import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from '../../../styles';
const ApplicationSettings = () => { const ApplicationSettings = () => {
const endpoint = 'api/apps'; const endpoint = 'api/apps';

View File

@@ -1,23 +1,16 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { BookmarkColumns, DataTable } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { DataTable } from '../../../DataTable/DataTable'; import { textClasses } from 'styles';
import BookmarkColumns from '../../../DataTable/columns/BookmarkColumns';
import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from '../../../styles';
const BookmarkSettings = () => { const BookmarkSettings = () => {
const endpoint = 'api/links'; const endpoint = 'api/links';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
const { const { data: groupData } = useQueryAPI({ endpoint: 'api/categories' });
data: groupData,
isFetching: groupDataIsFetching,
isLoading: groupDataIsLoading,
isFetched: groupDataIsFetched,
} = useQueryAPI({ endpoint: 'api/categories' });
const [bookmarkData, setBookmarkData] = useState<any>([]); const [bookmarkData, setBookmarkData] = useState<any>([]);
const [groups, setGroups] = useState([]); const [groups, setGroups] = useState([]);
const [fullyFetched, setFullyFetched] = useState(false);
useEffect(() => { useEffect(() => {
if (isFetched) { if (isFetched) {
@@ -31,7 +24,6 @@ const BookmarkSettings = () => {
return category.categoryName; return category.categoryName;
}); });
setGroups(groupList); setGroups(groupList);
setFullyFetched(true);
} }
}, [groupData]); }, [groupData]);
return ( return (
@@ -46,7 +38,6 @@ const BookmarkSettings = () => {
isFetching={isFetching} isFetching={isFetching}
isLoading={isLoading} isLoading={isLoading}
endpoint={endpoint} endpoint={endpoint}
grouping='category'
/> />
</Stack> </Stack>
); );

View File

@@ -1,7 +1,6 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { CategoryColumns, DataTable } from 'components';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { DataTable } from '../../../DataTable/DataTable';
import CategoryColumns from '../../../DataTable/columns/CategoryColumns';
import { useQueryAPI } from '../../../hooks/useAPI'; import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from '../../../styles'; import { textClasses } from '../../../styles';
const CategorySettings = () => { const CategorySettings = () => {

View File

@@ -1,9 +1,8 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { DataTable, MessagesColumns } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { DataTable } from '../../../DataTable/DataTable'; import { textClasses } from 'styles';
import MessagesColumns from '../../../DataTable/columns/MessagesColumns';
import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from '../../../styles';
const MessageSettings = () => { const MessageSettings = () => {
const endpoint = 'api/messages'; const endpoint = 'api/messages';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({

View File

@@ -1,9 +1,8 @@
import { Text } from '@mantine/core'; import { Text } from '@mantine/core';
import { DataTable, PersonalizationColumns } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { DataTable } from '../../../DataTable/DataTable'; import { textClasses } from 'styles';
import PersonalizationColumns from '../../../DataTable/columns/PersonalizationColumns';
import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from '../../../styles';
const PersonalizationSettings = () => { const PersonalizationSettings = () => {
const endpoint = 'api/personalData'; const endpoint = 'api/personalData';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({

View File

@@ -1,9 +1,9 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { DataTable, SearchProviderColumns } from 'components';
import { useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { DataTable } from '../../../DataTable/DataTable'; import { textClasses } from 'styles';
import SearchProviderColumns from '../../../DataTable/columns/SearchProviderColumns';
import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from '../../../styles';
const SearchProviderSettings = () => { const SearchProviderSettings = () => {
const endpoint = 'api/providers'; const endpoint = 'api/providers';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({

View File

@@ -1,7 +1,7 @@
import { Icon } from '@iconify/react/dist/iconify.js'; import { Icon } from '@iconify/react/dist/iconify.js';
import { Box, Group, Stack, Text } from '@mantine/core'; import { Box, Group, Stack, Text } from '@mantine/core';
import { useState } from 'react'; import { useState } from 'react';
import { stackClasses } from '../../../styles'; import { stackClasses } from 'styles';
export default function ThemeModalContent({ selectedTheme }: any) { export default function ThemeModalContent({ selectedTheme }: any) {
const ExampleApp = (previewTheme: any) => { const ExampleApp = (previewTheme: any) => {

View File

@@ -12,25 +12,20 @@ import {
TextInput, TextInput,
} from '@mantine/core'; } from '@mantine/core';
import { useDisclosure, useLocalStorage } from '@mantine/hooks'; import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiEdit, FiPlus, FiSave, FiTrash, FiX } from 'react-icons/fi'; import { FiEdit, FiPlus, FiSave, FiTrash, FiX } from 'react-icons/fi';
import { useMutationAPI, useQueryAPI } from '../../../hooks/useAPI';
import '@mantine/core/styles/ModalBase.css'; import '@mantine/core/styles/ModalBase.css';
import '@mantine/core/styles/Modal.css'; import '@mantine/core/styles/Modal.css';
import { modals } from '@mantine/modals'; import { modals } from '@mantine/modals';
import { import { actionIconClasses, buttonClasses, modalClasses, textClasses } from 'styles';
actionIconClasses,
buttonClasses,
modalClasses,
textClasses,
} from '../../../styles';
import ThemeModalContent from './ThemeModalContent'; import ThemeModalContent from './ThemeModalContent';
function ThemesSettings({ theme }: any) { function ThemesSettings({ theme }: any) {
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
const [pickerOpen, { open: openPicker, close: closePicker }] = useDisclosure(false); const [pickerOpen, { open: openPicker, close: closePicker }] = useDisclosure(false);
const [colorId, setColorId] = useState<string>(''); const [colorId, setColorId] = useState<string>('');
const [colorValue, setColorValue] = useState('#000000'); const [colorValue, setColorValue] = useState('#000000');
const [value, setValue] = useLocalStorage<any>({ const [_, setValue] = useLocalStorage<any>({
key: 'theme', key: 'theme',
defaultValue: 'blackboard', defaultValue: 'blackboard',
}); });
@@ -39,8 +34,8 @@ function ThemesSettings({ theme }: any) {
}); });
const [themesList, setThemesList] = useState([]); const [themesList, setThemesList] = useState([]);
const [selectedTheme, setSelectedTheme] = useState<any>({}); const [selectedTheme, setSelectedTheme] = useState<any>({});
const { mutateAsync: createTheme, isPending: isCreatingTheme } = useMutationAPI(); const { mutateAsync: createTheme } = useMutationAPI();
const { mutateAsync: deleteTheme, isPending: isDeletingTheme } = useMutationAPI(); const { mutateAsync: deleteTheme } = useMutationAPI();
useEffect(() => { useEffect(() => {
if (isFetched) { if (isFetched) {
setThemesList(data); setThemesList(data);

1
src/types/index.ts Normal file
View File

@@ -0,0 +1 @@
export { type CurrentWeather, type OutlookWeather } from './weather';

View File

@@ -2,12 +2,18 @@
"compilerOptions": { "compilerOptions": {
"target": "ES2020", "target": "ES2020",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
"baseUrl": "./src",
"rootDir": "./src",
"allowJs": false,
"esModuleInterop": false,
"lib": ["ESNext", "DOM"],
"allowSyntheticDefaultImports": true,
"types": ["vite/client", "vitest"],
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "bundler", "moduleResolution": "node",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
@@ -18,7 +24,15 @@
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true,
"paths": {
"components": ["./components/index.ts"],
"contexts": ["./contexts/index.ts"],
"hooks": ["./hooks/index.ts"],
"pages": ["./pages/index.ts"],
"styles": ["./styles/index.ts"],
"types": ["./types/index.ts"]
}
}, },
"include": ["src"], "include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]

View File

@@ -1,3 +1,4 @@
import path from 'node:path';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import { defineConfig, loadEnv } from 'vite'; import { defineConfig, loadEnv } from 'vite';
@@ -28,5 +29,15 @@ export default defineConfig(({ mode }) => {
}, },
}, },
}, },
resolve: {
alias: {
components: path.resolve(__dirname, './src/components'),
hooks: path.resolve(__dirname, './src/hooks'),
pages: path.resolve(__dirname, './src/pages'),
styles: path.resolve(__dirname, './src/styles'),
types: path.resolve(__dirname, './src/types'),
contexts: path.resolve(__dirname, './src/contexts'),
},
},
}; };
}); });