Refactor
This commit is contained in:
@@ -4,5 +4,3 @@
|
||||
- Create actions section
|
||||
|
||||
- Fix all query/setState problems
|
||||
|
||||
- Random "feeling lucky" or subreddit button
|
||||
|
||||
1934
package-lock.json
generated
1934
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,12 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.9",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
11
src/App.tsx
11
src/App.tsx
@@ -7,6 +7,7 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import { ModalsProvider } from '@mantine/modals';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
Route,
|
||||
@@ -14,24 +15,22 @@ import {
|
||||
createBrowserRouter,
|
||||
createRoutesFromElements,
|
||||
} from 'react-router-dom';
|
||||
import ThemeProvider from './components/ThemeProvider';
|
||||
import { useQueryAPI } from './hooks/useAPI';
|
||||
import Homepage from './pages/homepage/Homepage';
|
||||
import Settings from './pages/settings/Settings';
|
||||
import { ThemeProvider } from './components';
|
||||
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 { appTheme } from './contexts/theme';
|
||||
import './index.css';
|
||||
import { Homepage, Settings } from 'pages';
|
||||
|
||||
function App() {
|
||||
const { data, isFetched } = useQueryAPI({
|
||||
endpoint: 'api/themes',
|
||||
});
|
||||
|
||||
const [themeName, setThemeName] = useLocalStorage<any>({
|
||||
const [themeName, _] = useLocalStorage<any>({
|
||||
key: 'theme',
|
||||
defaultValue: 'blackboard',
|
||||
});
|
||||
|
||||
@@ -5,15 +5,15 @@ import {
|
||||
useMantineReactTable,
|
||||
} from 'mantine-react-table';
|
||||
import { memo } from 'react';
|
||||
import '../dataTableStyles.css';
|
||||
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 { FiPlus } from 'react-icons/fi';
|
||||
import { appTheme } from '../contexts/theme';
|
||||
import { useMutationAPI } from '../hooks/useAPI';
|
||||
import { actionIconClasses, buttonClasses } from '../styles';
|
||||
import { actionIconClasses, buttonClasses } from 'styles';
|
||||
|
||||
export function DataTableComponent({
|
||||
tableName,
|
||||
@@ -22,7 +22,6 @@ export function DataTableComponent({
|
||||
isFetching,
|
||||
isLoading,
|
||||
endpoint,
|
||||
grouping,
|
||||
}: {
|
||||
tableName: string;
|
||||
columns: any;
|
||||
@@ -30,9 +29,8 @@ export function DataTableComponent({
|
||||
isFetching: boolean;
|
||||
isLoading: boolean;
|
||||
endpoint: string;
|
||||
grouping?: string;
|
||||
}) {
|
||||
const [theme, setTheme] = useAtom(appTheme);
|
||||
const [theme, _] = useAtom(appTheme);
|
||||
const { mutateAsync: createRow } = useMutationAPI();
|
||||
const { mutateAsync: editRow } = useMutationAPI();
|
||||
const { mutateAsync: deleteRow } = useMutationAPI();
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MRT_ColumnDef } from 'mantine-react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function ApplicationColumns() {
|
||||
export function ApplicationColumns() {
|
||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -15,7 +15,7 @@ export default function ApplicationColumns() {
|
||||
id: 'id',
|
||||
header: 'ID',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -27,7 +27,7 @@ export default function ApplicationColumns() {
|
||||
},
|
||||
id: 'name',
|
||||
header: 'Name',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -39,7 +39,7 @@ export default function ApplicationColumns() {
|
||||
},
|
||||
id: 'url',
|
||||
header: 'Url',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -51,7 +51,7 @@ export default function ApplicationColumns() {
|
||||
},
|
||||
id: 'icon',
|
||||
header: 'Icon',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -63,7 +63,7 @@ export default function ApplicationColumns() {
|
||||
},
|
||||
id: 'baseUrl',
|
||||
header: 'Base Url',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -75,7 +75,7 @@ export default function ApplicationColumns() {
|
||||
},
|
||||
id: 'filterName',
|
||||
header: 'Filter Name',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
],
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MRT_ColumnDef } from 'mantine-react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function ApplicationColumns(groups: any) {
|
||||
export function BookmarkColumns(groups: any) {
|
||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -15,7 +15,7 @@ export default function ApplicationColumns(groups: any) {
|
||||
id: 'id',
|
||||
header: 'ID',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -27,7 +27,7 @@ export default function ApplicationColumns(groups: any) {
|
||||
},
|
||||
id: 'name',
|
||||
header: 'Name',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -39,7 +39,7 @@ export default function ApplicationColumns(groups: any) {
|
||||
},
|
||||
id: 'url',
|
||||
header: 'Url',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MRT_ColumnDef } from 'mantine-react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function CategoryColumns() {
|
||||
export function CategoryColumns() {
|
||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -15,7 +15,7 @@ export default function CategoryColumns() {
|
||||
id: 'id',
|
||||
header: 'ID',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -28,7 +28,7 @@ export default function CategoryColumns() {
|
||||
id: 'categoryName',
|
||||
header: 'Name',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -42,7 +42,7 @@ export default function CategoryColumns() {
|
||||
id: 'categoryIndex',
|
||||
header: 'Index',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -55,7 +55,7 @@ export default function CategoryColumns() {
|
||||
id: 'filterName',
|
||||
header: 'Filter Name',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
],
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MRT_ColumnDef } from 'mantine-react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function MessagesColumns() {
|
||||
export function MessagesColumns() {
|
||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -15,7 +15,7 @@ export default function MessagesColumns() {
|
||||
id: 'id',
|
||||
header: 'ID',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -27,7 +27,7 @@ export default function MessagesColumns() {
|
||||
},
|
||||
id: 'message',
|
||||
header: 'Message',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
],
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MRT_ColumnDef } from 'mantine-react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function PersonalizationColumns() {
|
||||
export function PersonalizationColumns() {
|
||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -14,7 +14,7 @@ export default function PersonalizationColumns() {
|
||||
id: 'firstName',
|
||||
header: 'First Name',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -26,7 +26,7 @@ export default function PersonalizationColumns() {
|
||||
},
|
||||
id: 'lastName',
|
||||
header: 'Last Name',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -38,7 +38,7 @@ export default function PersonalizationColumns() {
|
||||
},
|
||||
id: 'email',
|
||||
header: 'Email',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
],
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MRT_ColumnDef } from 'mantine-react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function SearchProviderColumns() {
|
||||
export function SearchProviderColumns() {
|
||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -15,7 +15,7 @@ export default function SearchProviderColumns() {
|
||||
id: 'id',
|
||||
header: 'ID',
|
||||
size: 50,
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -27,7 +27,7 @@ export default function SearchProviderColumns() {
|
||||
},
|
||||
id: 'name',
|
||||
header: 'Name',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -39,7 +39,7 @@ export default function SearchProviderColumns() {
|
||||
},
|
||||
id: 'url',
|
||||
header: 'Url',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
{
|
||||
@@ -51,7 +51,7 @@ export default function SearchProviderColumns() {
|
||||
},
|
||||
id: 'prefix',
|
||||
header: 'Prefix',
|
||||
Cell: ({ renderedCellValue, row }) => renderedCellValue,
|
||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
||||
Header: ({ column }) => column.columnDef.header,
|
||||
},
|
||||
],
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Divider, Group, Modal, Stack, Text } from '@mantine/core';
|
||||
import { appTheme } from 'contexts';
|
||||
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,
|
||||
setProviderLabel,
|
||||
setProviderURL,
|
||||
@@ -11,7 +11,7 @@ export default function HelpModal({
|
||||
providerModalOpened,
|
||||
closeProviderModal,
|
||||
}: any) {
|
||||
const [theme, setTheme] = useAtom(appTheme);
|
||||
const [theme, _] = useAtom(appTheme);
|
||||
return (
|
||||
<Modal.Root
|
||||
size={'60%'}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Box } from '@mantine/core';
|
||||
|
||||
export default function ThemeProvider({
|
||||
export function ThemeProvider({
|
||||
theme,
|
||||
children,
|
||||
}: { theme: any; children: React.ReactNode }) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { stackClasses } from '../styles';
|
||||
import { stackClasses } from 'styles';
|
||||
|
||||
export function ToastError({ err }: { err: any }) {
|
||||
const errorResponse = JSON.parse(err.message);
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function HourlyCard({ weather }: any) {
|
||||
transform: `rotate(${WindDirection[weather.windDirection]}deg)`,
|
||||
}}
|
||||
/>
|
||||
{`${weather.probabilityOfPrecipitation.value * 10}`}%
|
||||
{`${weather.probabilityOfPrecipitation.value}`}%
|
||||
</Group>
|
||||
</Text>
|
||||
</Stack>
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Center, Divider, Group, Modal, Stack } from '@mantine/core';
|
||||
import { useAtom } from 'jotai';
|
||||
import { modalClasses } from '../../../styles';
|
||||
import { modalClasses } from 'styles';
|
||||
import SmallOutlookCard from './SmallOutlookCard';
|
||||
import SmallWeatherCard from './SmallWeatherCard';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { appTheme } from '../../../contexts/theme';
|
||||
import { appTheme } from '../../contexts/theme';
|
||||
import HourlyCard from './HourlyCard';
|
||||
|
||||
export default function WeatherModal({
|
||||
export function WeatherModal({
|
||||
current,
|
||||
outlook,
|
||||
hourly,
|
||||
@@ -18,7 +18,7 @@ export default function WeatherModal({
|
||||
setProviderLabel,
|
||||
setProviderURL,
|
||||
}: any) {
|
||||
const [theme, setTheme] = useAtom(appTheme);
|
||||
const [theme, _] = useAtom(appTheme);
|
||||
const [hourlyForecast, setHourlyForecast] = useState<any>([]);
|
||||
const [sunset, setSunset] = useState(100);
|
||||
|
||||
@@ -30,9 +30,14 @@ export default function WeatherModal({
|
||||
|
||||
useEffect(() => {
|
||||
if (hourlyForecast) {
|
||||
setSunset(
|
||||
(hourlyForecast.findIndex((item: any) => item.isDaytime === false) / 12) * 100,
|
||||
);
|
||||
hourlyForecast[0]?.isDaytime
|
||||
? setSunset(
|
||||
(hourlyForecast.findIndex((item: any) => item.isDaytime === false) / 12) *
|
||||
100,
|
||||
)
|
||||
: setSunset(
|
||||
(hourlyForecast.findIndex((item: any) => item.isDaytime === true) / 12) * 100,
|
||||
);
|
||||
}
|
||||
}, [hourlyForecast]);
|
||||
return (
|
||||
10
src/components/index.ts
Normal file
10
src/components/index.ts
Normal 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
2
src/contexts/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { HomepageFilters } from './HomepageFilters';
|
||||
export { appTheme } from './theme';
|
||||
47
src/hooks/helpers.ts
Normal file
47
src/hooks/helpers.ts
Normal 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);
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
export { useQueryAPI, useWeatherAPI } from './useAPI';
|
||||
export { useQueryAPI, useWeatherAPI, useMutationAPI } from './useAPI';
|
||||
export { date, greet } from './helpers';
|
||||
export { validURL, containsProtocol } from './helpers';
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useCallback } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
@@ -48,19 +43,7 @@ export function useWeatherAPI({ endpoint }: { endpoint: string }) {
|
||||
} else if (endpoint === 'hourly') {
|
||||
url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast/hourly';
|
||||
}
|
||||
const callEndpoint = async (endpoint: string) => {
|
||||
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 callEndpoint = async () => {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`${response.status} ${response.statusText}`);
|
||||
@@ -70,7 +53,7 @@ export function useWeatherAPI({ endpoint }: { endpoint: string }) {
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['GET', url],
|
||||
queryFn: () => callEndpoint(endpoint),
|
||||
queryFn: () => callEndpoint(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -343,89 +326,8 @@ interface DownloadFileOptions {
|
||||
}
|
||||
|
||||
export function useDownloadFile() {
|
||||
const downloadFile = useCallback(
|
||||
async ({ endpoint, file_name }: DownloadFileOptions) => {
|
||||
const options: 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',
|
||||
},
|
||||
};
|
||||
const toastID = toast.loading('Processing...');
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
if (blob.type === 'application/json') {
|
||||
const blob_text = await blob.text();
|
||||
const cleaned_text = blob_text.replace(/"/g, '');
|
||||
toast.update(toastID, {
|
||||
render: cleaned_text,
|
||||
type: 'info',
|
||||
isLoading: false,
|
||||
autoClose: 5000,
|
||||
closeButton: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const blobURL = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
|
||||
a.style.display = 'none';
|
||||
a.href = blobURL;
|
||||
a.download = file_name;
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(blobURL);
|
||||
document.body.removeChild(a);
|
||||
toast.update(toastID, {
|
||||
render: `${file_name} Downloaded`,
|
||||
type: 'success',
|
||||
isLoading: false,
|
||||
autoClose: 5000,
|
||||
closeButton: null,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Download failed:', error);
|
||||
toast.update(toastID, {
|
||||
render: `Error Downloading ${file_name}. Please try again.`,
|
||||
type: 'error',
|
||||
isLoading: false,
|
||||
autoClose: 5000,
|
||||
closeButton: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return downloadFile;
|
||||
}
|
||||
|
||||
export const useFetchInfiniteQuery = ({
|
||||
endpoint,
|
||||
pageSize = 10,
|
||||
}: {
|
||||
endpoint: string;
|
||||
pageSize?: number;
|
||||
}) => {
|
||||
const queryKey = ['GET', endpoint];
|
||||
|
||||
const fetchInfiniteQuery = async (endpoint: string) => {
|
||||
const requestOptions: any = {
|
||||
const downloadFile = useCallback(async ({ file_name }: DownloadFileOptions) => {
|
||||
const options: any = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -437,35 +339,58 @@ export const useFetchInfiniteQuery = ({
|
||||
'Sec-Fetch-Site': 'same-site',
|
||||
},
|
||||
};
|
||||
const toastID = toast.loading('Processing...');
|
||||
try {
|
||||
const response = await fetch(url, requestOptions);
|
||||
return await response.json();
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
if (blob.type === 'application/json') {
|
||||
const blob_text = await blob.text();
|
||||
const cleaned_text = blob_text.replace(/"/g, '');
|
||||
toast.update(toastID, {
|
||||
render: cleaned_text,
|
||||
type: 'info',
|
||||
isLoading: false,
|
||||
autoClose: 5000,
|
||||
closeButton: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const blobURL = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
|
||||
a.style.display = 'none';
|
||||
a.href = blobURL;
|
||||
a.download = file_name;
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(blobURL);
|
||||
document.body.removeChild(a);
|
||||
toast.update(toastID, {
|
||||
render: `${file_name} Downloaded`,
|
||||
type: 'success',
|
||||
isLoading: false,
|
||||
autoClose: 5000,
|
||||
closeButton: null,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching transactions:', error);
|
||||
throw error;
|
||||
console.error('Download failed:', error);
|
||||
toast.update(toastID, {
|
||||
render: `Error Downloading ${file_name}. Please try again.`,
|
||||
type: 'error',
|
||||
isLoading: false,
|
||||
autoClose: 5000,
|
||||
closeButton: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
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,
|
||||
};
|
||||
};
|
||||
}, []);
|
||||
|
||||
return downloadFile;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { Stack } from '@mantine/core';
|
||||
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
|
||||
import { date, greet, useQueryAPI, useWeatherAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useQueryAPI, useWeatherAPI } from '../../hooks/useAPI';
|
||||
import stackClasses from '../../styles/Stack.module.css';
|
||||
import Applications from './components/Applications';
|
||||
import Bookmarks from './components/Bookmarks';
|
||||
import Header from './components/Header';
|
||||
import Search from './components/Search';
|
||||
import { stackClasses } from 'styles';
|
||||
import type { CurrentWeather, OutlookWeather } from 'types';
|
||||
import Applications from './components/Applications/Applications';
|
||||
import Bookmarks from './components/Bookmarks/Bookmarks';
|
||||
import Header from './components/Header/Header';
|
||||
import Search from './components/Search/Search';
|
||||
import SettingsButton from './components/SettingsButton';
|
||||
import type { CurrentWeather, OutlookWeather } from './types/weather';
|
||||
|
||||
export default function Homepage({ theme }: any) {
|
||||
const { data, isFetching } = useQueryAPI({ endpoint: 'api/messages' });
|
||||
const [displayedGreeting, setDisplayedGreeting] = useState('');
|
||||
export function Homepage({ theme }: any) {
|
||||
const [appFilter, setAppFilter] = useLocalStorage<[any]>({
|
||||
key: 'appFilter',
|
||||
defaultValue: ['All'],
|
||||
@@ -21,8 +19,12 @@ export default function Homepage({ theme }: 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' });
|
||||
const [displayedGreeting, setDisplayedGreeting] = useState('');
|
||||
const [current, setCurrent] = useState<CurrentWeather>({
|
||||
icon: '',
|
||||
temperature: 0,
|
||||
@@ -45,42 +47,9 @@ export default function Homepage({ theme }: any) {
|
||||
windDirection: 'N',
|
||||
},
|
||||
]);
|
||||
const { data: forecast } = useWeatherAPI({ endpoint: 'forecast' });
|
||||
const { data: hourly } = useWeatherAPI({ endpoint: 'hourly' });
|
||||
const isMobile = window.matchMedia('(max-width: 600px)').matches;
|
||||
const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
|
||||
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(() => {
|
||||
if (data) {
|
||||
@@ -160,12 +129,12 @@ export default function Homepage({ theme }: any) {
|
||||
openWeatherModal={openWeatherModal}
|
||||
/>
|
||||
<Header
|
||||
date={date()}
|
||||
date={date(isMobile)}
|
||||
greet={displayedGreeting}
|
||||
isMobile={isMobile}
|
||||
current={current}
|
||||
outlook={outlook}
|
||||
setCurrent={setCurrent}
|
||||
outlook={outlook}
|
||||
weather={weather}
|
||||
basicWeather={basicWeather}
|
||||
forecast={forecast}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import { Box, Stack, Text } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import stacks from '../../../styles/Stack.module.css';
|
||||
import texts from '../../../styles/Text.module.css';
|
||||
import { stackClasses, textClasses } from 'styles';
|
||||
|
||||
function AppItem({ url, icon, name, isMobile }: any) {
|
||||
const [textDecoration, setTextDecoration] = useState<string>('none');
|
||||
@@ -13,7 +12,7 @@ function AppItem({ url, icon, name, isMobile }: any) {
|
||||
href={`https://${url}`}
|
||||
onMouseOver={() => setTextDecoration('underline')}
|
||||
onMouseOut={() => setTextDecoration('none')}
|
||||
className={stacks.appItemBox}
|
||||
className={stackClasses.appItemBox}
|
||||
>
|
||||
<Stack
|
||||
align={isMobile && 'center'}
|
||||
@@ -38,12 +37,12 @@ function AppItem({ url, icon, name, isMobile }: any) {
|
||||
gap: 0,
|
||||
}}
|
||||
>
|
||||
<Text className={texts.appName} classNames={texts}>
|
||||
<Text className={textClasses.appName} classNames={textClasses}>
|
||||
{name}
|
||||
</Text>
|
||||
<Text
|
||||
className={texts.appUrl}
|
||||
classNames={texts}
|
||||
className={textClasses.appUrl}
|
||||
classNames={textClasses}
|
||||
style={{
|
||||
textDecoration: textDecoration,
|
||||
}}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import texts from '../../../styles/Text.module.css';
|
||||
import { textClasses } from 'styles';
|
||||
import AppItem from './AppItem';
|
||||
|
||||
function Applications({ contentType, isMobile }: any) {
|
||||
@@ -16,7 +16,7 @@ function Applications({ contentType, isMobile }: any) {
|
||||
<>
|
||||
{applications.length > 0 && contentType.length > 0 && (
|
||||
<Stack id='apps' style={{ gap: 0 }}>
|
||||
<Text className={texts.sectionTitle} classNames={texts}>
|
||||
<Text className={textClasses.sectionTitle} classNames={textClasses}>
|
||||
Applications
|
||||
</Text>
|
||||
<Stack
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import texts from '../../../styles/Text.module.css';
|
||||
import { textClasses } from 'styles';
|
||||
import LinkGroups from './LinkGroups';
|
||||
|
||||
function Links({ contentType, isMobile }: any) {
|
||||
@@ -27,7 +27,7 @@ function Links({ contentType, isMobile }: any) {
|
||||
categories?.length > 0 &&
|
||||
contentType.length > 0 && (
|
||||
<Stack id='links' style={{ gap: 0 }}>
|
||||
<Text className={texts.sectionTitle} classNames={texts}>
|
||||
<Text className={textClasses.sectionTitle} classNames={textClasses}>
|
||||
Bookmarks
|
||||
</Text>
|
||||
<Stack
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import texts from '../../../styles/Text.module.css';
|
||||
import { textClasses } from 'styles';
|
||||
import LinkItem from './LinkItem';
|
||||
|
||||
function LinkGroups({ links, category, isMobile }: any) {
|
||||
@@ -12,7 +12,7 @@ function LinkGroups({ links, category, isMobile }: any) {
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Text className={texts.linkCategory} classNames={texts}>
|
||||
<Text className={textClasses.linkCategory} classNames={textClasses}>
|
||||
{category}
|
||||
</Text>
|
||||
{links?.map((link: any) => (
|
||||
@@ -1,11 +1,11 @@
|
||||
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 (
|
||||
<Text
|
||||
className={texts.linkItem}
|
||||
classNames={texts}
|
||||
className={textClasses.linkItem}
|
||||
classNames={textClasses}
|
||||
component='a'
|
||||
href={link.url}
|
||||
style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Group, Stack, Text } from '@mantine/core';
|
||||
import { WeatherModal } from 'components';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { textClasses } from '../../../styles';
|
||||
import WeatherModal from './WeatherModal';
|
||||
import { textClasses } from 'styles';
|
||||
|
||||
function Header({
|
||||
greet,
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Stack, TextInput } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
import { useQueryAPI } from '../../../hooks';
|
||||
import { textInputClasses } from '../../../styles';
|
||||
import HelpModal from './HelpModal';
|
||||
import WeatherModal from './WeatherModal';
|
||||
import { HelpModal } from '../../../../components/HelpModal/HelpModal';
|
||||
import { WeatherModal } from '../../../../components/WeatherModal/WeatherModal';
|
||||
import { containsProtocol, useQueryAPI, validURL } from '../../../../hooks';
|
||||
import { textInputClasses } from '../../../../styles';
|
||||
|
||||
export default function Search({
|
||||
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 (
|
||||
<Stack
|
||||
style={{
|
||||
@@ -1,18 +1,14 @@
|
||||
import { Button, Menu, MultiSelect, NavLink } from '@mantine/core';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { FiMenu, FiMonitor } from 'react-icons/fi';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
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';
|
||||
import { buttonClasses, menuClasses, navLinkClasses, selectClasses } from 'styles';
|
||||
|
||||
function SettingsButton({
|
||||
appFilter,
|
||||
setAppFilter,
|
||||
bookmarkFilter,
|
||||
setBookmarkFilter,
|
||||
theme,
|
||||
}: any) {
|
||||
const { data, isFetched } = useQueryAPI({ endpoint: 'api/appCategories' });
|
||||
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);
|
||||
@@ -38,29 +34,29 @@ function SettingsButton({
|
||||
return (
|
||||
<Menu withinPortal={false} withArrow shadow='md' position='top'>
|
||||
<Menu.Target>
|
||||
<Button className={button.settingsButton} classNames={button}>
|
||||
<Button className={buttonClasses.settingsButton} classNames={buttonClasses}>
|
||||
<FiMenu />
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown className={menu.dropdown}>
|
||||
<Menu.Dropdown className={menuClasses.dropdown}>
|
||||
<NavLink
|
||||
label='Settings'
|
||||
component='a'
|
||||
href={`/${import.meta.env.VITE_BASE_URL}/settings`}
|
||||
className={navLink.defaultNavLink}
|
||||
classNames={navLink}
|
||||
className={navLinkClasses.defaultNavLink}
|
||||
classNames={navLinkClasses}
|
||||
/>
|
||||
<NavLink
|
||||
label='Quick Actions'
|
||||
className={navLink.defaultNavLink}
|
||||
classNames={navLink}
|
||||
className={navLinkClasses.defaultNavLink}
|
||||
classNames={navLinkClasses}
|
||||
>
|
||||
<NavLink
|
||||
label='Wake Dakota'
|
||||
leftSection={<FiMonitor />}
|
||||
onClick={() => runWakeCommand()}
|
||||
className={navLink.defaultNavLink}
|
||||
classNames={navLink}
|
||||
className={navLinkClasses.defaultNavLink}
|
||||
classNames={navLinkClasses}
|
||||
/>
|
||||
</NavLink>
|
||||
<MultiSelect
|
||||
|
||||
2
src/pages/index.ts
Normal file
2
src/pages/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { Homepage } from './homepage/Homepage';
|
||||
export { Settings } from './settings/Settings';
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ActionIcon, Group, Stack, Tabs, Text } from '@mantine/core';
|
||||
import { FiArrowLeftCircle } from 'react-icons/fi';
|
||||
import { actionIconClasses, textClasses } from '../../styles';
|
||||
import tabClasses from '../../styles/Tabs.module.css';
|
||||
import { actionIconClasses, tabClasses, textClasses } from 'styles';
|
||||
import Applications from './components/Applications';
|
||||
import Bookmarks from './components/Bookmarks';
|
||||
import Categories from './components/Categories';
|
||||
@@ -10,7 +9,7 @@ import Personalization from './components/Personalization';
|
||||
import SearchProviders from './components/SearchProviders';
|
||||
import Themes from './components/Themes';
|
||||
|
||||
function Settings({ theme }: any) {
|
||||
export function Settings({ theme }: any) {
|
||||
return (
|
||||
<Stack h={'100vh'} px={'2rem'}>
|
||||
<Group gap={0}>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { ApplicationColumns, DataTable } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DataTable } from '../../../DataTable/DataTable';
|
||||
import ApplicationColumns from '../../../DataTable/columns/ApplicationColumns';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import { textClasses } from '../../../styles';
|
||||
import { textClasses } from 'styles';
|
||||
|
||||
const ApplicationSettings = () => {
|
||||
const endpoint = 'api/apps';
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { BookmarkColumns, DataTable } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DataTable } from '../../../DataTable/DataTable';
|
||||
import BookmarkColumns from '../../../DataTable/columns/BookmarkColumns';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import { textClasses } from '../../../styles';
|
||||
import { textClasses } from 'styles';
|
||||
const BookmarkSettings = () => {
|
||||
const endpoint = 'api/links';
|
||||
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
|
||||
endpoint: endpoint,
|
||||
});
|
||||
const {
|
||||
data: groupData,
|
||||
isFetching: groupDataIsFetching,
|
||||
isLoading: groupDataIsLoading,
|
||||
isFetched: groupDataIsFetched,
|
||||
} = useQueryAPI({ endpoint: 'api/categories' });
|
||||
const { data: groupData } = useQueryAPI({ endpoint: 'api/categories' });
|
||||
const [bookmarkData, setBookmarkData] = useState<any>([]);
|
||||
const [groups, setGroups] = useState([]);
|
||||
const [fullyFetched, setFullyFetched] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFetched) {
|
||||
@@ -31,7 +24,6 @@ const BookmarkSettings = () => {
|
||||
return category.categoryName;
|
||||
});
|
||||
setGroups(groupList);
|
||||
setFullyFetched(true);
|
||||
}
|
||||
}, [groupData]);
|
||||
return (
|
||||
@@ -46,7 +38,6 @@ const BookmarkSettings = () => {
|
||||
isFetching={isFetching}
|
||||
isLoading={isLoading}
|
||||
endpoint={endpoint}
|
||||
grouping='category'
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { CategoryColumns, DataTable } from 'components';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DataTable } from '../../../DataTable/DataTable';
|
||||
import CategoryColumns from '../../../DataTable/columns/CategoryColumns';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import { textClasses } from '../../../styles';
|
||||
const CategorySettings = () => {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { DataTable, MessagesColumns } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DataTable } from '../../../DataTable/DataTable';
|
||||
import MessagesColumns from '../../../DataTable/columns/MessagesColumns';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import { textClasses } from '../../../styles';
|
||||
import { textClasses } from 'styles';
|
||||
const MessageSettings = () => {
|
||||
const endpoint = 'api/messages';
|
||||
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Text } from '@mantine/core';
|
||||
import { DataTable, PersonalizationColumns } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DataTable } from '../../../DataTable/DataTable';
|
||||
import PersonalizationColumns from '../../../DataTable/columns/PersonalizationColumns';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import { textClasses } from '../../../styles';
|
||||
import { textClasses } from 'styles';
|
||||
const PersonalizationSettings = () => {
|
||||
const endpoint = 'api/personalData';
|
||||
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { DataTable, SearchProviderColumns } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DataTable } from '../../../DataTable/DataTable';
|
||||
import SearchProviderColumns from '../../../DataTable/columns/SearchProviderColumns';
|
||||
import { useQueryAPI } from '../../../hooks/useAPI';
|
||||
import { textClasses } from '../../../styles';
|
||||
import { textClasses } from 'styles';
|
||||
|
||||
const SearchProviderSettings = () => {
|
||||
const endpoint = 'api/providers';
|
||||
const { data, isFetching, isLoading, isFetched } = useQueryAPI({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js';
|
||||
import { Box, Group, Stack, Text } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { stackClasses } from '../../../styles';
|
||||
import { stackClasses } from 'styles';
|
||||
|
||||
export default function ThemeModalContent({ selectedTheme }: any) {
|
||||
const ExampleApp = (previewTheme: any) => {
|
||||
|
||||
@@ -12,25 +12,20 @@ import {
|
||||
TextInput,
|
||||
} from '@mantine/core';
|
||||
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
|
||||
import { useMutationAPI, useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
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/Modal.css';
|
||||
import { modals } from '@mantine/modals';
|
||||
import {
|
||||
actionIconClasses,
|
||||
buttonClasses,
|
||||
modalClasses,
|
||||
textClasses,
|
||||
} from '../../../styles';
|
||||
import { actionIconClasses, buttonClasses, modalClasses, textClasses } from 'styles';
|
||||
import ThemeModalContent from './ThemeModalContent';
|
||||
function ThemesSettings({ theme }: any) {
|
||||
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 [value, setValue] = useLocalStorage<any>({
|
||||
const [_, setValue] = useLocalStorage<any>({
|
||||
key: 'theme',
|
||||
defaultValue: 'blackboard',
|
||||
});
|
||||
@@ -39,8 +34,8 @@ function ThemesSettings({ theme }: any) {
|
||||
});
|
||||
const [themesList, setThemesList] = useState([]);
|
||||
const [selectedTheme, setSelectedTheme] = useState<any>({});
|
||||
const { mutateAsync: createTheme, isPending: isCreatingTheme } = useMutationAPI();
|
||||
const { mutateAsync: deleteTheme, isPending: isDeletingTheme } = useMutationAPI();
|
||||
const { mutateAsync: createTheme } = useMutationAPI();
|
||||
const { mutateAsync: deleteTheme } = useMutationAPI();
|
||||
useEffect(() => {
|
||||
if (isFetched) {
|
||||
setThemesList(data);
|
||||
|
||||
1
src/types/index.ts
Normal file
1
src/types/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { type CurrentWeather, type OutlookWeather } from './weather';
|
||||
@@ -2,12 +2,18 @@
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "./src",
|
||||
"rootDir": "./src",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"types": ["vite/client", "vitest"],
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"moduleResolution": "node",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
@@ -18,7 +24,15 @@
|
||||
"strict": true,
|
||||
"noUnusedLocals": 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"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from 'node:path';
|
||||
import react from '@vitejs/plugin-react';
|
||||
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'),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user