Toasts working
This commit is contained in:
@@ -21,6 +21,7 @@ import 'mantine-react-table/styles.css'; //import MRT styles
|
|||||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import { Homepage, Settings } from 'pages';
|
import { Homepage, Settings } from 'pages';
|
||||||
|
import { ToastContainer } from 'react-toastify';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { data: allThemes, isFetched: isAllThemesFetched } = useQueryAPI({
|
const { data: allThemes, isFetched: isAllThemesFetched } = useQueryAPI({
|
||||||
@@ -65,6 +66,7 @@ function App() {
|
|||||||
{theme && (
|
{theme && (
|
||||||
<MantineProvider theme={mantineTheme}>
|
<MantineProvider theme={mantineTheme}>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
|
<ToastContainer />
|
||||||
<ModalsProvider>
|
<ModalsProvider>
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
<ReactQueryDevtools initialIsOpen={false} />
|
<ReactQueryDevtools initialIsOpen={false} />
|
||||||
|
|||||||
@@ -12,6 +12,56 @@ export function ThemeProvider({
|
|||||||
'--color-background': theme.backgroundColor,
|
'--color-background': theme.backgroundColor,
|
||||||
'--color-border': theme.borderColor,
|
'--color-border': theme.borderColor,
|
||||||
'--color-search-background': theme.searchColor,
|
'--color-search-background': theme.searchColor,
|
||||||
|
'--toastify-color-light': `hwb(from ${theme.primaryColor} h w b / 0.25)`,
|
||||||
|
'--toastify-color-dark': `hwb(from ${theme.primaryColor} h w b / 0.25)`,
|
||||||
|
'--toastify-color-info': '#3498db',
|
||||||
|
'--toastify-color-success': '#07bc0c',
|
||||||
|
'--toastify-color-warning': '#f1c40f',
|
||||||
|
'--toastify-color-error': 'hsl(6, 78%, 57%)',
|
||||||
|
'--toastify-color-transparent': 'rgba(255, 255, 255, 0.7)',
|
||||||
|
'--toastify-icon-color-info': 'var(--toastify-color-info)',
|
||||||
|
'--toastify-icon-color-success': 'var(--toastify-color-success)',
|
||||||
|
'--toastify-icon-color-warning': 'var(--toastify-color-warning)',
|
||||||
|
'--toastify-icon-color-error': 'var(--toastify-color-error)',
|
||||||
|
|
||||||
|
'--toastify-container-width': 'fit-content',
|
||||||
|
'--toastify-toast-width': '320px',
|
||||||
|
'--toastify-toast-offset': '16px',
|
||||||
|
'--toastify-toast-top':
|
||||||
|
'max(var(--toastify-toast-offset), env(safe-area-inset-top))',
|
||||||
|
'--toastify-toast-right':
|
||||||
|
'max(var(--toastify-toast-offset), env(safe-area-inset-right))',
|
||||||
|
'--toastify-toast-left':
|
||||||
|
'max(var(--toastify-toast-offset), env(safe-area-inset-left))',
|
||||||
|
'--toastify-toast-bottom':
|
||||||
|
'max(var(--toastify-toast-offset), env(safe-area-inset-bottom))',
|
||||||
|
'--toastify-toast-background': '#fff',
|
||||||
|
'--toastify-toast-padding': '14px',
|
||||||
|
'--toastify-toast-min-height': '64px',
|
||||||
|
'--toastify-toast-max-height': '800px',
|
||||||
|
'--toastify-toast-bd-radius': '6px',
|
||||||
|
'--toastify-toast-shadow': '0px 4px 12px rgba(0, 0, 0, 0.1)',
|
||||||
|
'--toastify-font-family': 'sans-serif',
|
||||||
|
'--toastify-z-index': '9999',
|
||||||
|
'--toastify-text-color-light': theme.primaryColor,
|
||||||
|
'--toastify-text-color-dark': theme.primaryColor,
|
||||||
|
|
||||||
|
/* Used only for colored theme */
|
||||||
|
'--toastify-text-color-info': '#fff',
|
||||||
|
'--toastify-text-color-success': '#fff',
|
||||||
|
'--toastify-text-color-warning': '#fff',
|
||||||
|
'--toastify-text-color-error': '#fff',
|
||||||
|
|
||||||
|
'--toastify-spinner-color': '#616161',
|
||||||
|
'--toastify-spinner-color-empty-area': '#e0e0e0',
|
||||||
|
'--toastify-color-progress-light': theme.accentColor,
|
||||||
|
'--toastify-color-progress-dark': '#bb86fc',
|
||||||
|
'--toastify-color-progress-info': 'var(--toastify-color-info)',
|
||||||
|
'--toastify-color-progress-success': 'var(--toastify-color-success)',
|
||||||
|
'--toastify-color-progress-warning': 'var(--toastify-color-warning)',
|
||||||
|
'--toastify-color-progress-error': 'var(--toastify-color-error)',
|
||||||
|
/* used to control the opacity of the progress trail */
|
||||||
|
'--toastify-color-progress-bgo': 0.2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Stack, Text } from '@mantine/core';
|
|
||||||
import { stackClasses } from 'styles';
|
|
||||||
|
|
||||||
export function ToastError({ err }: { err: any }) {
|
|
||||||
const errorResponse = JSON.parse(err.message);
|
|
||||||
return (
|
|
||||||
<Stack className={stackClasses.VStack} style={{ alignItems: 'flex-start' }}>
|
|
||||||
<Text>
|
|
||||||
Status: {errorResponse.status} {errorResponse.statusText}
|
|
||||||
</Text>
|
|
||||||
<Text>Details: {errorResponse.msg}</Text>
|
|
||||||
{errorResponse.loc && <Text>Location: {errorResponse.loc}</Text>}
|
|
||||||
{errorResponse.type && <Text>Type: {errorResponse.type}</Text>}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { useCommonHooks } from 'hooks';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
const url = `${import.meta.env.PROD ? import.meta.env.VITE_DOMAIN_PROD : import.meta.env.VITE_DOMAIN_DEV}/${import.meta.env.VITE_BASE_URL}`;
|
const url = `${import.meta.env.PROD ? import.meta.env.VITE_DOMAIN_PROD : import.meta.env.VITE_DOMAIN_DEV}/${import.meta.env.VITE_BASE_URL}`;
|
||||||
|
|
||||||
@@ -32,32 +32,8 @@ export function useQueryAPI({ endpoint }: { endpoint: string }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWeatherAPI({ endpoint }: { endpoint: string }) {
|
|
||||||
let url = '';
|
|
||||||
if (endpoint === 'baseline') {
|
|
||||||
url = 'https://api.weather.gov/gridpoints/HGX/55,96';
|
|
||||||
} else if (endpoint === 'current') {
|
|
||||||
url = 'https://api.weather.gov/stations/KSGR/observations';
|
|
||||||
} else if (endpoint === 'forecast') {
|
|
||||||
url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast';
|
|
||||||
} else if (endpoint === 'hourly') {
|
|
||||||
url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast/hourly';
|
|
||||||
}
|
|
||||||
const callEndpoint = async () => {
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
return await response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
return useQuery({
|
|
||||||
queryKey: ['GET', url],
|
|
||||||
queryFn: () => callEndpoint(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useMutationAPI() {
|
export function useMutationAPI() {
|
||||||
|
const { notify } = useCommonHooks();
|
||||||
const callEndpoint = async (
|
const callEndpoint = async (
|
||||||
method: string,
|
method: string,
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
@@ -76,9 +52,10 @@ export function useMutationAPI() {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
};
|
};
|
||||||
|
notify('Sending...');
|
||||||
const response = await fetch(`${url}/${endpoint}`, options);
|
const response = await fetch(`${url}/${endpoint}`, options);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
notify('Error!');
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
if (typeof responseData.detail !== 'string') {
|
if (typeof responseData.detail !== 'string') {
|
||||||
const errorResponse = JSON.stringify({
|
const errorResponse = JSON.stringify({
|
||||||
@@ -114,6 +91,7 @@ export function useMutationAPI() {
|
|||||||
|
|
||||||
throw new Error(errorResponse);
|
throw new Error(errorResponse);
|
||||||
}
|
}
|
||||||
|
notify('Success!');
|
||||||
return await response.json();
|
return await response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,6 +115,31 @@ export function useMutationAPI() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useWeatherAPI({ endpoint }: { endpoint: string }) {
|
||||||
|
let url = '';
|
||||||
|
if (endpoint === 'baseline') {
|
||||||
|
url = 'https://api.weather.gov/gridpoints/HGX/55,96';
|
||||||
|
} else if (endpoint === 'current') {
|
||||||
|
url = 'https://api.weather.gov/stations/KSGR/observations';
|
||||||
|
} else if (endpoint === 'forecast') {
|
||||||
|
url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast';
|
||||||
|
} else if (endpoint === 'hourly') {
|
||||||
|
url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast/hourly';
|
||||||
|
}
|
||||||
|
const callEndpoint = async () => {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return await response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['GET', url],
|
||||||
|
queryFn: () => callEndpoint(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function useFileUploadAPI() {
|
export function useFileUploadAPI() {
|
||||||
const callEndpoint = async (
|
const callEndpoint = async (
|
||||||
method: string,
|
method: string,
|
||||||
@@ -319,78 +322,3 @@ export function useFetchAllRecordsAPI({ endpoint }: { endpoint: string }) {
|
|||||||
invalidate,
|
invalidate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DownloadFileOptions {
|
|
||||||
endpoint: string;
|
|
||||||
file_name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDownloadFile() {
|
|
||||||
const downloadFile = useCallback(async ({ 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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useLocalStorage } from '@mantine/hooks';
|
|||||||
import { appTheme } from 'contexts';
|
import { appTheme } from 'contexts';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
import { useQueryAPI, useWeatherAPI } from './useAPI';
|
import { useQueryAPI, useWeatherAPI } from './useAPI';
|
||||||
|
|
||||||
export function useCommonHooks() {
|
export function useCommonHooks() {
|
||||||
@@ -95,6 +96,8 @@ export function useCommonHooks() {
|
|||||||
|
|
||||||
const [applicationData, setApplicationData] = useState<any>([]);
|
const [applicationData, setApplicationData] = useState<any>([]);
|
||||||
|
|
||||||
|
const notify = (notification: any) => toast(notification, { position: 'bottom-right' });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (apps && !isAppsFetching) {
|
if (apps && !isAppsFetching) {
|
||||||
setApplicationData(apps);
|
setApplicationData(apps);
|
||||||
@@ -200,5 +203,6 @@ export function useCommonHooks() {
|
|||||||
personalIsFetched,
|
personalIsFetched,
|
||||||
personalIsFetching,
|
personalIsFetching,
|
||||||
personalIsLoading,
|
personalIsLoading,
|
||||||
|
notify,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,3 +10,7 @@
|
|||||||
background-color: var(--color-text-pri) !important;
|
background-color: var(--color-text-pri) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Toastify__toast-theme--light {
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ function SettingsButton() {
|
|||||||
bookmarkFilter,
|
bookmarkFilter,
|
||||||
setBookmarkFilter,
|
setBookmarkFilter,
|
||||||
isMobile,
|
isMobile,
|
||||||
|
notify,
|
||||||
} = useCommonHooks();
|
} = useCommonHooks();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -87,7 +88,10 @@ function SettingsButton() {
|
|||||||
<Button
|
<Button
|
||||||
classNames={buttonClasses}
|
classNames={buttonClasses}
|
||||||
className={buttonClasses.wakeButton}
|
className={buttonClasses.wakeButton}
|
||||||
onClick={() => runWakeCommand()}
|
onClick={() => {
|
||||||
|
runWakeCommand();
|
||||||
|
notify('Wake command sent');
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap={0} align='center'>
|
<Stack gap={0} align='center'>
|
||||||
<FiMonitor size={40} />
|
<FiMonitor size={40} />
|
||||||
|
|||||||
@@ -68,6 +68,6 @@
|
|||||||
.menuStack {
|
.menuStack {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
background-color: hwb(from var(--color-text-pri) h w b / 0.15);
|
background-color: var(--color-search-background);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user