(feat:settings) smoother transitions

This commit is contained in:
ManishMadan2882
2025-02-11 23:55:30 +05:30
parent ea0a6e413d
commit ac447dd055
4 changed files with 184 additions and 124 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import userService from '../api/services/userService';
@@ -11,18 +11,28 @@ import SkeletonLoader from '../components/SkeletonLoader';
export default function APIKeys() {
const { t } = useTranslation();
const [isCreateModalOpen, setCreateModal] = React.useState(false);
const [isSaveKeyModalOpen, setSaveKeyModal] = React.useState(false);
const [newKey, setNewKey] = React.useState('');
const [apiKeys, setApiKeys] = React.useState<APIKeyData[]>([]);
const [loading, setLoading] = useState(true);
const [isCreateModalOpen, setCreateModal] = useState(false);
const [isSaveKeyModalOpen, setSaveKeyModal] = useState(false);
const [newKey, setNewKey] = useState('');
const [apiKeys, setApiKeys] = useState<APIKeyData[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [keyToDelete, setKeyToDelete] = useState<{
id: string;
name: string;
} | null>(null);
const handleFetchKeys = async () => {
setLoading(true);
const setLoadingWithMinDuration = useCallback((isLoading: boolean) => {
if (isLoading) {
setLoading(true);
} else {
setTimeout(() => {
setLoading(false);
}, 2000);
}
}, []);
const handleFetchKeys = useCallback(async () => {
setLoadingWithMinDuration(true);
try {
const response = await userService.getAPIKeys();
if (!response.ok) {
@@ -33,60 +43,81 @@ export default function APIKeys() {
} catch (error) {
console.log(error);
} finally {
setLoading(false);
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);
}
};
const handleDeleteKey = (id: string) => {
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);
});
};
const handleCreateKey = (payload: {
name: string;
source?: string;
retriever?: string;
prompt_id: string;
chunks: string;
}) => {
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);
});
};
React.useEffect(() => {
handleFetchKeys();
}, []);
return (
<div className="mt-8">
<div className="flex flex-col max-w-[876px]">
@@ -118,7 +149,7 @@ export default function APIKeys() {
modalState="ACTIVE"
setModalState={() => setKeyToDelete(null)}
submitLabel={t('modals.deleteConv.delete')}
handleSubmit={() => handleDeleteKey(keyToDelete.id)}
handleSubmit={confirmDelete}
handleCancel={() => setKeyToDelete(null)}
/>
)}
@@ -161,7 +192,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 } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import {
BarElement,
@@ -88,9 +88,25 @@ export default function Analytics() {
value: 'last_30_days',
});
const [loadingMessages, setLoadingMessages] = useState(true);
const [loadingTokens, setLoadingTokens] = useState(true);
const [loadingFeedback, setLoadingFeedback] = useState(true);
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 fetchChatbots = async () => {
try {
@@ -105,84 +121,75 @@ export default function Analytics() {
}
};
const fetchMessagesData = async (chatbot_id?: string, filter?: string) => {
setLoadingMessages(true);
const fetchMessagesData = useCallback(async () => {
setLoadingWithMinDuration(setLoadingMessages, true);
try {
const response = await userService.getMessageAnalytics({
api_key_id: chatbot_id,
filter_option: filter,
api_key_id: selectedChatbot?.id,
filter_option: messagesFilter.value,
});
if (!response.ok) {
throw new Error('Failed to fetch analytics data');
}
const data = await response.json();
setMessagesData(data.messages);
setLoadingWithMinDuration(setLoadingMessages, false);
} catch (error) {
console.error(error);
} finally {
setLoadingMessages(false);
setLoadingWithMinDuration(setLoadingMessages, false);
}
};
}, [selectedChatbot, messagesFilter, setLoadingWithMinDuration]);
const fetchTokenData = async (chatbot_id?: string, filter?: string) => {
setLoadingTokens(true);
const fetchTokenData = useCallback(async () => {
setLoadingWithMinDuration(setLoadingTokens, true);
try {
const response = await userService.getTokenAnalytics({
api_key_id: chatbot_id,
filter_option: filter,
api_key_id: selectedChatbot?.id,
filter_option: tokenUsageFilter.value,
});
if (!response.ok) {
throw new Error('Failed to fetch analytics data');
}
const data = await response.json();
setTokenUsageData(data.token_usage);
setLoadingWithMinDuration(setLoadingTokens, false);
} catch (error) {
console.error(error);
} finally {
setLoadingTokens(false);
setLoadingWithMinDuration(setLoadingTokens, false);
}
};
}, [selectedChatbot, tokenUsageFilter, setLoadingWithMinDuration]);
const fetchFeedbackData = async (chatbot_id?: string, filter?: string) => {
setLoadingFeedback(true);
const fetchFeedbackData = useCallback(async () => {
setLoadingWithMinDuration(setLoadingFeedback, true);
try {
const response = await userService.getFeedbackAnalytics({
api_key_id: chatbot_id,
filter_option: filter,
api_key_id: selectedChatbot?.id,
filter_option: feedbackFilter.value,
});
if (!response.ok) {
throw new Error('Failed to fetch analytics data');
}
const data = await response.json();
setFeedbackData(data.feedback);
setLoadingWithMinDuration(setLoadingFeedback, false);
} catch (error) {
console.error(error);
} finally {
setLoadingFeedback(false);
setLoadingWithMinDuration(setLoadingFeedback, false);
}
};
}, [selectedChatbot, feedbackFilter, setLoadingWithMinDuration]);
useEffect(() => {
fetchChatbots();
}, []);
useEffect(() => {
const id = selectedChatbot?.id;
const filter = messagesFilter;
fetchMessagesData(id, filter?.value);
}, [selectedChatbot, messagesFilter]);
useEffect(() => {
const id = selectedChatbot?.id;
const filter = tokenUsageFilter;
fetchTokenData(id, filter?.value);
}, [selectedChatbot, tokenUsageFilter]);
useEffect(() => {
const id = selectedChatbot?.id;
const filter = feedbackFilter;
fetchFeedbackData(id, filter?.value);
}, [selectedChatbot, feedbackFilter]);
fetchMessagesData();
fetchTokenData();
fetchFeedbackData();
}, [fetchMessagesData, fetchTokenData, fetchFeedbackData]);
return (
<div className="mt-12">

View File

@@ -7,11 +7,11 @@ import caretSort from '../assets/caret-sort.svg';
import DropdownMenu from '../components/DropdownMenu';
import SkeletonLoader from '../components/SkeletonLoader';
import Input from '../components/Input';
import Upload from '../upload/Upload'; // Import the Upload component
import Upload from '../upload/Upload';
import Pagination from '../components/DocumentPagination';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { Doc, DocumentsProps, ActiveState } from '../models/misc'; // Ensure ActiveState type is imported
import { Doc, DocumentsProps, ActiveState } from '../models/misc';
import { getDocs, getDocsWithPagination } from '../preferences/preferenceApi';
import { setSourceDocs } from '../preferences/preferenceSlice';
import { setPaginatedDocuments } from '../preferences/preferenceSlice';
@@ -41,15 +41,13 @@ const Documents: React.FC<DocumentsProps> = ({
}) => {
const { t } = useTranslation();
const dispatch = useDispatch();
// State for search input
const [searchTerm, setSearchTerm] = useState<string>('');
// State for modal: active/inactive
const [modalState, setModalState] = useState<ActiveState>('INACTIVE'); // Initialize with inactive state
const [isOnboarding, setIsOnboarding] = useState<boolean>(false); // State for onboarding flag
const [modalState, setModalState] = useState<ActiveState>('INACTIVE');
const [isOnboarding, setIsOnboarding] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const [sortField, setSortField] = useState<'date' | 'tokens'>('date');
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
// Pagination
const [currentPage, setCurrentPage] = useState<number>(1);
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
const [totalPages, setTotalPages] = useState<number>(1);
@@ -61,6 +59,16 @@ const Documents: React.FC<DocumentsProps> = ({
{ label: t('settings.documents.syncFrequency.monthly'), value: 'monthly' },
];
const setLoadingWithMinDuration = useCallback((isLoading: boolean) => {
if (isLoading) {
setLoading(true);
} else {
setTimeout(() => {
setLoading(false);
}, 2000);
}
}, []);
const refreshDocs = useCallback(
(
field: 'date' | 'tokens' | undefined,
@@ -69,11 +77,7 @@ const Documents: React.FC<DocumentsProps> = ({
) => {
const page = pageNumber ?? currentPage;
const rowsPerPg = rows ?? rowsPerPage;
// If field is undefined, (Pagination or Search) use the current sortField
const newSortField = field ?? sortField;
// If field is undefined, (Pagination or Search) use the current sortOrder
const newSortOrder =
field === sortField
? sortOrder === 'asc'
@@ -81,13 +85,12 @@ const Documents: React.FC<DocumentsProps> = ({
: 'asc'
: sortOrder;
// If field is defined, update the sortField and sortOrder
if (field) {
setSortField(newSortField);
setSortOrder(newSortOrder);
}
setLoading(true);
setLoadingWithMinDuration(true);
getDocsWithPagination(
newSortField,
newSortOrder,
@@ -98,17 +101,26 @@ const Documents: React.FC<DocumentsProps> = ({
.then((data) => {
dispatch(setPaginatedDocuments(data ? data.docs : []));
setTotalPages(data ? data.totalPages : 0);
setLoadingWithMinDuration(false);
})
.catch((error) => console.error(error))
.finally(() => {
setLoading(false);
.catch((error) => {
console.error(error);
setLoadingWithMinDuration(false);
});
},
[currentPage, rowsPerPage, sortField, sortOrder, searchTerm],
[
currentPage,
rowsPerPage,
sortField,
sortOrder,
searchTerm,
dispatch,
setLoadingWithMinDuration,
],
);
const handleManageSync = (doc: Doc, sync_frequency: string) => {
setLoading(true);
setLoadingWithMinDuration(true);
userService
.manageSync({ source_id: doc.id, sync_frequency })
.then(() => {
@@ -128,10 +140,11 @@ const Documents: React.FC<DocumentsProps> = ({
setPaginatedDocuments(paginatedData ? paginatedData.docs : []),
);
setTotalPages(paginatedData ? paginatedData.totalPages : 0);
setLoadingWithMinDuration(false);
})
.catch((error) => console.error('Error in handleManageSync:', error))
.finally(() => {
setLoading(false);
.catch((error) => {
console.error('Error in handleManageSync:', error);
setLoadingWithMinDuration(false);
});
};
@@ -202,7 +215,6 @@ const Documents: React.FC<DocumentsProps> = ({
) : (
<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>
@@ -249,7 +261,7 @@ const Documents: React.FC<DocumentsProps> = ({
<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-gray-300 bg-transparent"
>
{t('settings.documents.noData')}
</td>

View File

@@ -18,6 +18,16 @@ export default function Logs() {
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 fetchChatbots = async () => {
setLoadingChatbots(true);
try {
@@ -35,7 +45,7 @@ export default function Logs() {
};
const fetchLogs = async () => {
setLoadingLogs(true);
setLoadingLogsWithMinDuration(true);
try {
const response = await userService.getLogs({
page: page,
@@ -51,7 +61,7 @@ export default function Logs() {
} catch (error) {
console.error(error);
} finally {
setLoadingLogs(false);
setLoadingLogsWithMinDuration(false);
}
};