From 181bf69994990a15b8c9949b80d6973eee31ef9b Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 13 Feb 2025 17:16:17 +0530 Subject: [PATCH] (feat:transitions) custom hook to loading state --- frontend/src/hooks/index.ts | 20 ++++ frontend/src/settings/APIKeys.tsx | 148 ++++++++++++---------------- frontend/src/settings/Analytics.tsx | 34 ++----- frontend/src/settings/Documents.tsx | 28 ++---- frontend/src/settings/Logs.tsx | 19 +--- 5 files changed, 105 insertions(+), 144 deletions(-) diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index b993f3da0..56f50406a 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -112,3 +112,23 @@ export function useDarkTheme() { return [isDarkTheme, toggleTheme, componentMounted] as const; } + +export function useLoaderState( + initialState = false, + delay = 500, +): [boolean, (value: boolean) => void] { + const [state, setState] = useState(initialState); + + const setLoaderState = (value: boolean) => { + if (value) { + setState(true); + } else { + // Only add delay when changing from true to false + setTimeout(() => { + setState(false); + }, delay); + } + }; + + return [state, setLoaderState]; +} diff --git a/frontend/src/settings/APIKeys.tsx b/frontend/src/settings/APIKeys.tsx index 748cadb87..2226544c4 100644 --- a/frontend/src/settings/APIKeys.tsx +++ b/frontend/src/settings/APIKeys.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import userService from '../api/services/userService'; @@ -8,6 +8,7 @@ import SaveAPIKeyModal from '../modals/SaveAPIKeyModal'; import ConfirmationModal from '../modals/ConfirmationModal'; import { APIKeyData } from './types'; import SkeletonLoader from '../components/SkeletonLoader'; +import { useLoaderState } from '../hooks'; export default function APIKeys() { const { t } = useTranslation(); @@ -15,24 +16,14 @@ export default function APIKeys() { const [isSaveKeyModalOpen, setSaveKeyModal] = useState(false); const [newKey, setNewKey] = useState(''); const [apiKeys, setApiKeys] = useState([]); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useLoaderState(true); const [keyToDelete, setKeyToDelete] = useState<{ id: string; name: string; } | null>(null); - const setLoadingWithMinDuration = useCallback((isLoading: boolean) => { - if (isLoading) { - setLoading(true); - } else { - setTimeout(() => { - setLoading(false); - }, 2000); - } - }, []); - - const handleFetchKeys = useCallback(async () => { - setLoadingWithMinDuration(true); + const handleFetchKeys = async () => { + setLoading(true); try { const response = await userService.getAPIKeys(); if (!response.ok) { @@ -43,80 +34,67 @@ export default function APIKeys() { } catch (error) { console.log(error); } finally { - setLoadingWithMinDuration(false); + setLoading(false); } - }, [setLoadingWithMinDuration]); + }; - const handleDeleteKey = useCallback( - (id: string) => { - setLoadingWithMinDuration(true); - userService - .deleteAPIKey({ id }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to delete API Key'); - } - return response.json(); - }) - .then((data) => { - if (data.success === true) { - setApiKeys((previous) => previous.filter((elem) => elem.id !== id)); - } - setKeyToDelete(null); - }) - .catch((error) => { - console.error(error); - }) - .finally(() => { - setLoadingWithMinDuration(false); - }); - }, - [setLoadingWithMinDuration], - ); + const handleDeleteKey = (id: string) => { + setLoading(true); + userService + .deleteAPIKey({ id }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to delete API Key'); + } + return response.json(); + }) + .then((data) => { + data.success === true && + setApiKeys((previous) => previous.filter((elem) => elem.id !== id)); + setKeyToDelete(null); + }) + .catch((error) => { + console.error(error); + }) + .finally(() => { + setLoading(false); + }); + }; - const handleCreateKey = useCallback( - (payload: { - name: string; - source?: string; - retriever?: string; - prompt_id: string; - chunks: string; - }) => { - setLoadingWithMinDuration(true); - userService - .createAPIKey(payload) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to create API Key'); - } - return response.json(); - }) - .then((data) => { - setApiKeys((prevKeys) => [...prevKeys, data]); - setCreateModal(false); - setNewKey(data.key); - setSaveKeyModal(true); - handleFetchKeys(); - }) - .catch((error) => { - console.error(error); - }) - .finally(() => { - setLoadingWithMinDuration(false); - }); - }, - [handleFetchKeys, setLoadingWithMinDuration], - ); + const handleCreateKey = (payload: { + name: string; + source?: string; + retriever?: string; + prompt_id: string; + chunks: string; + }) => { + setLoading(true); + userService + .createAPIKey(payload) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to create API Key'); + } + return response.json(); + }) + .then((data) => { + setApiKeys([...apiKeys, data]); + setCreateModal(false); + setNewKey(data.key); + setSaveKeyModal(true); + handleFetchKeys(); + }) + .catch((error) => { + console.error(error); + }) + .finally(() => { + setLoading(false); + }); + }; - useEffect(() => { + React.useEffect(() => { handleFetchKeys(); - }, [handleFetchKeys]); - - const confirmDelete = () => { - if (keyToDelete) { - handleDeleteKey(keyToDelete.id); - } - }; + }, []); return (
@@ -149,7 +127,7 @@ export default function APIKeys() { modalState="ACTIVE" setModalState={() => setKeyToDelete(null)} submitLabel={t('modals.deleteConv.delete')} - handleSubmit={confirmDelete} + handleSubmit={() => handleDeleteKey(keyToDelete.id)} handleCancel={() => setKeyToDelete(null)} /> )} @@ -192,7 +170,7 @@ export default function APIKeys() { )} {Array.isArray(apiKeys) && - apiKeys.map((element, index) => ( + apiKeys?.map((element, index) => ( (false); - const [loadingTokens, setLoadingTokens] = useState(false); - const [loadingFeedback, setLoadingFeedback] = useState(false); - - const setLoadingWithMinDuration = useCallback( - ( - setter: React.Dispatch>, - isLoading: boolean, - ) => { - if (isLoading) { - setter(true); - } else { - setTimeout(() => { - setter(false); - }, 2000); - } - }, - [], - ); + const [loadingMessages, setLoadingMessages] = useLoaderState(true); + const [loadingTokens, setLoadingTokens] = useLoaderState(true); + const [loadingFeedback, setLoadingFeedback] = useLoaderState(true); const fetchChatbots = async () => { try { @@ -133,11 +118,10 @@ export default function Analytics() { } const data = await response.json(); setMessagesData(data.messages); - setLoadingWithMinDuration(setLoadingMessages, false); } catch (error) { console.error(error); } finally { - setLoadingWithMinDuration(setLoadingMessages, false); + setLoadingMessages(false); } }; @@ -153,11 +137,10 @@ export default function Analytics() { } const data = await response.json(); setTokenUsageData(data.token_usage); - setLoadingWithMinDuration(setLoadingTokens, false); } catch (error) { console.error(error); } finally { - setLoadingWithMinDuration(setLoadingTokens, false); + setLoadingTokens(false); } }; @@ -173,11 +156,10 @@ export default function Analytics() { } const data = await response.json(); setFeedbackData(data.feedback); - setLoadingWithMinDuration(setLoadingFeedback, false); } catch (error) { console.error(error); } finally { - setLoadingWithMinDuration(setLoadingFeedback, false); + setLoadingFeedback(false); } }; diff --git a/frontend/src/settings/Documents.tsx b/frontend/src/settings/Documents.tsx index 99b40dc96..32254a8fb 100644 --- a/frontend/src/settings/Documents.tsx +++ b/frontend/src/settings/Documents.tsx @@ -15,7 +15,7 @@ import DropdownMenu from '../components/DropdownMenu'; import Input from '../components/Input'; import SkeletonLoader from '../components/SkeletonLoader'; import Spinner from '../components/Spinner'; -import { useDarkTheme } from '../hooks'; +import { useDarkTheme, useLoaderState } from '../hooks'; import ChunkModal from '../modals/ChunkModal'; import ConfirmationModal from '../modals/ConfirmationModal'; import { ActiveState, Doc, DocumentsProps } from '../models/misc'; @@ -54,7 +54,7 @@ export default function Documents({ const [searchTerm, setSearchTerm] = useState(''); const [modalState, setModalState] = useState('INACTIVE'); const [isOnboarding, setIsOnboarding] = useState(false); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useLoaderState(false); const [sortField, setSortField] = useState<'date' | 'tokens'>('date'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); // Pagination @@ -70,16 +70,6 @@ export default function Documents({ ]; const [showDocumentChunks, setShowDocumentChunks] = useState(); - const setLoadingWithMinDuration = useCallback((isLoading: boolean) => { - if (isLoading) { - setLoading(true); - } else { - setTimeout(() => { - setLoading(false); - }, 2000); - } - }, []); - const refreshDocs = useCallback( ( field: 'date' | 'tokens' | undefined, @@ -106,7 +96,7 @@ export default function Documents({ setSortOrder(newSortOrder); } - setLoadingWithMinDuration(true); + setLoading(true); getDocsWithPagination( newSortField, newSortOrder, @@ -120,14 +110,14 @@ export default function Documents({ }) .catch((error) => console.error(error)) .finally(() => { - setLoadingWithMinDuration(false); + setLoading(false); }); }, [currentPage, rowsPerPage, sortField, sortOrder, searchTerm], ); const handleManageSync = (doc: Doc, sync_frequency: string) => { - setLoadingWithMinDuration(true); + setLoading(true); userService .manageSync({ source_id: doc.id, sync_frequency }) .then(() => { @@ -150,7 +140,7 @@ export default function Documents({ }) .catch((error) => console.error('Error in handleManageSync:', error)) .finally(() => { - setLoadingWithMinDuration(false); + setLoading(false); }); }; @@ -229,7 +219,6 @@ export default function Documents({ ) : (
{' '} - {/* Removed overflow-auto */}
@@ -276,7 +265,7 @@ export default function Documents({ @@ -405,7 +394,7 @@ function DocumentChunks({ const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(5); const [totalChunks, setTotalChunks] = useState(0); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useLoaderState(true); const [searchTerm, setSearchTerm] = useState(''); const [addModal, setAddModal] = useState('INACTIVE'); const [editModal, setEditModal] = useState<{ @@ -435,6 +424,7 @@ function DocumentChunks({ }); } catch (e) { console.log(e); + setLoading(false); } }; diff --git a/frontend/src/settings/Logs.tsx b/frontend/src/settings/Logs.tsx index a3e7b3fed..aa4e32a47 100644 --- a/frontend/src/settings/Logs.tsx +++ b/frontend/src/settings/Logs.tsx @@ -7,6 +7,7 @@ import Dropdown from '../components/Dropdown'; import SkeletonLoader from '../components/SkeletonLoader'; import { APIKeyData, LogData } from './types'; import CoppyButton from '../components/CopyButton'; +import { useLoaderState } from '../hooks'; export default function Logs() { const { t } = useTranslation(); @@ -15,18 +16,8 @@ export default function Logs() { const [logs, setLogs] = useState([]); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); - const [loadingChatbots, setLoadingChatbots] = useState(true); - const [loadingLogs, setLoadingLogs] = useState(true); - - const setLoadingLogsWithMinDuration = useCallback((isLoading: boolean) => { - if (isLoading) { - setLoadingLogs(true); - } else { - setTimeout(() => { - setLoadingLogs(false); - }, 2000); - } - }, []); + const [loadingChatbots, setLoadingChatbots] = useLoaderState(true); + const [loadingLogs, setLoadingLogs] = useLoaderState(true); const fetchChatbots = async () => { setLoadingChatbots(true); @@ -45,7 +36,7 @@ export default function Logs() { }; const fetchLogs = async () => { - setLoadingLogsWithMinDuration(true); + setLoadingLogs(true); try { const response = await userService.getLogs({ page: page, @@ -61,7 +52,7 @@ export default function Logs() { } catch (error) { console.error(error); } finally { - setLoadingLogsWithMinDuration(false); + setLoadingLogs(false); } };
{t('settings.documents.noData')}