(feat:transitions) custom hook to loading state

This commit is contained in:
ManishMadan2882
2025-02-13 17:16:17 +05:30
parent d08861fb30
commit 181bf69994
5 changed files with 108 additions and 147 deletions

View File

@@ -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<boolean>(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];
}

View File

@@ -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<APIKeyData[]>([]);
const [loading, setLoading] = useState<boolean>(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,81 +34,68 @@ export default function APIKeys() {
} catch (error) {
console.log(error);
} finally {
setLoadingWithMinDuration(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 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],
);
useEffect(() => {
handleFetchKeys();
}, [handleFetchKeys]);
const confirmDelete = () => {
if (keyToDelete) {
handleDeleteKey(keyToDelete.id);
setLoading(false);
}
};
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 = (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);
});
};
React.useEffect(() => {
handleFetchKeys();
}, []);
return (
<div className="mt-8">
<div className="flex flex-col max-w-[876px]">
@@ -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() {
</tr>
)}
{Array.isArray(apiKeys) &&
apiKeys.map((element, index) => (
apiKeys?.map((element, index) => (
<tr
key={index}
className="text-nowrap whitespace-nowrap text-center text-sm font-medium text-gray-800 dark:text-neutral-200 p-2"

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
BarElement,
@@ -16,6 +16,7 @@ import Dropdown from '../components/Dropdown';
import { htmlLegendPlugin } from '../utils/chartUtils';
import { formatDate } from '../utils/dateTimeUtils';
import { APIKeyData } from './types';
import { useLoaderState } from '../hooks';
import type { ChartData } from 'chart.js';
import SkeletonLoader from '../components/SkeletonLoader';
@@ -88,25 +89,9 @@ export default function Analytics() {
value: 'last_30_days',
});
const [loadingMessages, setLoadingMessages] = useState<boolean>(false);
const [loadingTokens, setLoadingTokens] = useState<boolean>(false);
const [loadingFeedback, setLoadingFeedback] = useState<boolean>(false);
const setLoadingWithMinDuration = useCallback(
(
setter: React.Dispatch<React.SetStateAction<boolean>>,
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);
}
};

View File

@@ -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<string>('');
const [modalState, setModalState] = useState<ActiveState>('INACTIVE');
const [isOnboarding, setIsOnboarding] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(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<Doc>();
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({
) : (
<div className="flex flex-col flex-grow">
{' '}
{/* Removed overflow-auto */}
<div className="border rounded-md border-gray-300 dark:border-silver/40 overflow-hidden">
<table className="w-full min-w-[640px] table-auto">
<thead>
@@ -276,7 +265,7 @@ export default function Documents({
<tr>
<td
colSpan={4}
className="py-4 text-center text-gray-700 dark:text-[#E0E0E] bg-transparent"
className="py-4 text-center text-gray-700 dark:text-neutral-200 bg-transparent"
>
{t('settings.documents.noData')}
</td>
@@ -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<string>('');
const [addModal, setAddModal] = useState<ActiveState>('INACTIVE');
const [editModal, setEditModal] = useState<{
@@ -435,6 +424,7 @@ function DocumentChunks({
});
} catch (e) {
console.log(e);
setLoading(false);
}
};

View File

@@ -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<LogData[]>([]);
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);
}
};