mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 16:43:16 +00:00
Merge branch 'arc53:main' into main
This commit is contained in:
@@ -19,7 +19,10 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
onPageChange,
|
||||
onRowsPerPageChange,
|
||||
}) => {
|
||||
const [rowsPerPageOptions] = useState([5, 10, 15, 20]);
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const rowsPerPageOptions = [5, 10, 20, 50];
|
||||
|
||||
const toggleDropdown = () => setIsDropdownOpen((prev) => !prev);
|
||||
|
||||
const handlePreviousPage = () => {
|
||||
if (currentPage > 1) {
|
||||
@@ -41,31 +44,51 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
onPageChange(totalPages);
|
||||
};
|
||||
|
||||
const handleSelectRowsPerPage = (rows: number) => {
|
||||
setIsDropdownOpen(false);
|
||||
onRowsPerPageChange(rows);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center text-xs justify-end gap-4 mt-2 p-2 border-gray-200">
|
||||
<div className="flex items-center gap-2 ">
|
||||
{/* Rows per page dropdown */}
|
||||
<div className="flex items-center gap-2 relative">
|
||||
<span className="text-gray-900 dark:text-gray-50">Rows per page:</span>
|
||||
<select
|
||||
value={rowsPerPage}
|
||||
onChange={(e) => onRowsPerPageChange(Number(e.target.value))}
|
||||
className="border border-gray-300 rounded px-2 py-1 dark:bg-dark-charcoal dark:text-gray-50"
|
||||
>
|
||||
{rowsPerPageOptions.map((option) => (
|
||||
<option
|
||||
className="bg-white dark:bg-dark-charcoal dark:text-gray-50"
|
||||
key={option}
|
||||
value={option}
|
||||
>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={toggleDropdown}
|
||||
className="px-3 py-1 border rounded dark:bg-dark-charcoal dark:text-light-gray hover:bg-gray-200 dark:hover:bg-neutral-700"
|
||||
>
|
||||
{rowsPerPage}
|
||||
</button>
|
||||
<div
|
||||
className={`absolute z-50 right-0 mt-1 w-28 transform bg-white dark:bg-dark-charcoal shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-200 ease-in-out ${
|
||||
isDropdownOpen
|
||||
? 'scale-100 opacity-100 block'
|
||||
: 'scale-95 opacity-0 hidden'
|
||||
}`}
|
||||
>
|
||||
{rowsPerPageOptions.map((option) => (
|
||||
<div
|
||||
key={option}
|
||||
onClick={() => handleSelectRowsPerPage(option)}
|
||||
className={`cursor-pointer px-4 py-2 text-xs hover:bg-gray-100 dark:hover:bg-neutral-700 ${
|
||||
rowsPerPage === option
|
||||
? 'bg-gray-100 dark:bg-neutral-700 dark:text-light-gray'
|
||||
: 'bg-white dark:bg-dark-charcoal dark:text-light-gray'
|
||||
}`}
|
||||
>
|
||||
{option}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pagination controls */}
|
||||
<div className="text-gray-900 dark:text-gray-50">
|
||||
Page {currentPage} of {totalPages}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-gray-900 dark:text-gray-50">
|
||||
<button
|
||||
onClick={handleFirstPage}
|
||||
@@ -74,7 +97,7 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
>
|
||||
<img
|
||||
src={DoubleArrowLeft}
|
||||
alt="arrow"
|
||||
alt="First page"
|
||||
className="dark:invert dark:sepia dark:brightness-200"
|
||||
/>
|
||||
</button>
|
||||
@@ -85,7 +108,7 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
>
|
||||
<img
|
||||
src={SingleArrowLeft}
|
||||
alt="arrow"
|
||||
alt="Previous page"
|
||||
className="dark:invert dark:sepia dark:brightness-200"
|
||||
/>
|
||||
</button>
|
||||
@@ -96,7 +119,7 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
>
|
||||
<img
|
||||
src={SingleArrowRight}
|
||||
alt="arrow"
|
||||
alt="Next page"
|
||||
className="dark:invert dark:sepia dark:brightness-200"
|
||||
/>
|
||||
</button>
|
||||
@@ -107,7 +130,7 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
>
|
||||
<img
|
||||
src={DoubleArrowRight}
|
||||
alt="arrow"
|
||||
alt="Last page"
|
||||
className="dark:invert dark:sepia dark:brightness-200"
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function DropdownMenu({
|
||||
<div className="static inline-block text-left" ref={dropdownRef}>
|
||||
<button
|
||||
onClick={handleToggle}
|
||||
className="flex w-20 cursor-pointer flex-row items-center gap-px rounded-3xl border-purple-30/25 bg-purple-30 p-2 text-xs text-white hover:bg-[#6F3FD1] focus:outline-none"
|
||||
className="flex w-20 cursor-pointer flex-row gap-1 rounded-3xl border-purple-30/25 bg-purple-30 p-2 text-xs text-white hover:bg-[#6F3FD1] focus:outline-none"
|
||||
>
|
||||
{icon && <img src={icon} alt="OptionIcon" className="h-4 w-4" />}
|
||||
{selectedOption.value !== 'never' ? selectedOption.label : name}
|
||||
|
||||
@@ -50,19 +50,23 @@ body.dark {
|
||||
|
||||
@layer components {
|
||||
.table-default {
|
||||
@apply block w-full mx-auto table-auto content-start justify-center rounded-xl border border-silver dark:border-silver/40 text-center dark:text-bright-gray overflow-auto;
|
||||
@apply block w-full table-auto content-start justify-center rounded-xl border border-silver dark:border-silver/40 text-center dark:text-bright-gray overflow-auto;
|
||||
}
|
||||
|
||||
.table-default th {
|
||||
@apply p-4 w-full font-normal text-gray-400 text-nowrap; /* Remove border-r */
|
||||
@apply p-4 font-normal text-gray-400 text-nowrap; /* Remove border-r */
|
||||
}
|
||||
|
||||
.table-default th {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.table-default th:last-child {
|
||||
@apply w-[auto];
|
||||
flex: 0; /* Ensure the last column does not stretch unnecessarily */
|
||||
}
|
||||
|
||||
.table-default td {
|
||||
@apply border-t border-silver dark:border-silver/40 px-4 py-2; /* Remove border-r */
|
||||
@apply border-t w-full border-silver dark:border-silver/40 px-4 py-2; /* Remove border-r */
|
||||
}
|
||||
|
||||
.table-default td:last-child {
|
||||
|
||||
@@ -25,9 +25,10 @@ export async function getDocsWithPagination(
|
||||
order = 'desc',
|
||||
pageNumber = 1,
|
||||
rowsPerPage = 10,
|
||||
searchTerm = '',
|
||||
): Promise<GetDocsResponse | null> {
|
||||
try {
|
||||
const query = `sort=${sort}&order=${order}&page=${pageNumber}&rows=${rowsPerPage}`;
|
||||
const query = `sort=${sort}&order=${order}&page=${pageNumber}&rows=${rowsPerPage}&search=${searchTerm}`;
|
||||
const response = await userService.getDocsWithPagination(query);
|
||||
const data = await response.json();
|
||||
const docs: Doc[] = [];
|
||||
|
||||
@@ -109,41 +109,56 @@ export default function APIKeys() {
|
||||
{loading ? (
|
||||
<SkeletonLoader count={1} component={'chatbot'} />
|
||||
) : (
|
||||
<table className="table-default">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('settings.apiKeys.name')}</th>
|
||||
<th>{t('settings.apiKeys.sourceDoc')}</th>
|
||||
<th>{t('settings.apiKeys.key')}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!apiKeys?.length && (
|
||||
<tr>
|
||||
<td colSpan={4} className="!p-4">
|
||||
{t('settings.apiKeys.noData')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{apiKeys?.map((element, index) => (
|
||||
<tr key={index}>
|
||||
<td>{element.name}</td>
|
||||
<td>{element.source}</td>
|
||||
<td>{element.key}</td>
|
||||
<td>
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
className="h-4 w-4 cursor-pointer hover:opacity-50"
|
||||
id={`img-${index}`}
|
||||
onClick={() => handleDeleteKey(element.id)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex-grow">
|
||||
<div className="dark:border-silver/40 border-silver rounded-md border overflow-auto">
|
||||
<table className="min-w-full divide-y divide-silver dark:divide-silver/40 ">
|
||||
<thead>
|
||||
<tr className="text-start text-sm font-medium text-gray-700 dark:text-gray-50 uppercase">
|
||||
<th className="p-2">{t('settings.apiKeys.name')}</th>
|
||||
<th className="p-2">
|
||||
{t('settings.apiKeys.sourceDoc')}
|
||||
</th>
|
||||
<th className="p-2">{t('settings.apiKeys.key')}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-neutral-700">
|
||||
{!apiKeys?.length && (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={4}
|
||||
className="!p-4 text-gray-800 dark:text-neutral-200 text-center"
|
||||
>
|
||||
{t('settings.apiKeys.noData')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{Array.isArray(apiKeys) &&
|
||||
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"
|
||||
>
|
||||
<td className="p-1">{element.name}</td>
|
||||
<td className="p-2">{element.source}</td>
|
||||
<td>{element.key}</td>
|
||||
<td>
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
className="h-4 w-4 cursor-pointer hover:opacity-50"
|
||||
id={`img-${index}`}
|
||||
onClick={() => handleDeleteKey(element.id)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import userService from '../api/services/userService';
|
||||
import SyncIcon from '../assets/sync.svg';
|
||||
@@ -40,25 +40,18 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
// State for search input
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [searchTerm, setSearchTerm] = useState<string>('');
|
||||
// State for modal: active/inactive
|
||||
const [modalState, setModalState] = useState<ActiveState>('INACTIVE'); // Initialize with inactive state
|
||||
const [isOnboarding, setIsOnboarding] = useState(false); // State for onboarding flag
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isOnboarding, setIsOnboarding] = useState<boolean>(false); // State for onboarding flag
|
||||
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);
|
||||
// const [totalDocuments, setTotalDocuments] = useState<number>(0);
|
||||
// Filter documents based on the search term
|
||||
const filteredDocuments = paginatedDocuments?.filter((document) =>
|
||||
document.name.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
);
|
||||
// State for documents
|
||||
const currentDocuments = filteredDocuments ?? [];
|
||||
console.log('currentDocuments', currentDocuments);
|
||||
const currentDocuments = paginatedDocuments ?? [];
|
||||
const syncOptions = [
|
||||
{ label: 'Never', value: 'never' },
|
||||
{ label: 'Daily', value: 'daily' },
|
||||
@@ -66,36 +59,50 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
{ label: 'Monthly', value: 'monthly' },
|
||||
];
|
||||
|
||||
const refreshDocs = (
|
||||
field: 'date' | 'tokens' | undefined,
|
||||
pageNumber?: number,
|
||||
rows?: number,
|
||||
) => {
|
||||
const page = pageNumber ?? currentPage;
|
||||
const rowsPerPg = rows ?? rowsPerPage;
|
||||
const refreshDocs = useCallback(
|
||||
(
|
||||
field: 'date' | 'tokens' | undefined,
|
||||
pageNumber?: number,
|
||||
rows?: number,
|
||||
) => {
|
||||
const page = pageNumber ?? currentPage;
|
||||
const rowsPerPg = rows ?? rowsPerPage;
|
||||
|
||||
if (field !== undefined) {
|
||||
if (field === sortField) {
|
||||
// Toggle sort order
|
||||
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
} else {
|
||||
// Change sort field and reset order to 'desc'
|
||||
setSortField(field);
|
||||
setSortOrder('desc');
|
||||
// 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'
|
||||
? 'desc'
|
||||
: 'asc'
|
||||
: sortOrder;
|
||||
|
||||
// If field is defined, update the sortField and sortOrder
|
||||
if (field) {
|
||||
setSortField(newSortField);
|
||||
setSortOrder(newSortOrder);
|
||||
}
|
||||
}
|
||||
getDocsWithPagination(sortField, sortOrder, page, rowsPerPg)
|
||||
.then((data) => {
|
||||
//dispatch(setSourceDocs(data ? data.docs : []));
|
||||
dispatch(setPaginatedDocuments(data ? data.docs : []));
|
||||
setTotalPages(data ? data.totalPages : 0);
|
||||
//setTotalDocuments(data ? data.totalDocuments : 0);
|
||||
})
|
||||
.catch((error) => console.error(error))
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
getDocsWithPagination(
|
||||
newSortField,
|
||||
newSortOrder,
|
||||
page,
|
||||
rowsPerPg,
|
||||
searchTerm,
|
||||
)
|
||||
.then((data) => {
|
||||
dispatch(setPaginatedDocuments(data ? data.docs : []));
|
||||
setTotalPages(data ? data.totalPages : 0);
|
||||
})
|
||||
.catch((error) => console.error(error))
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
},
|
||||
[currentPage, rowsPerPage, sortField, sortOrder, searchTerm],
|
||||
);
|
||||
|
||||
const handleManageSync = (doc: Doc, sync_frequency: string) => {
|
||||
setLoading(true);
|
||||
@@ -127,10 +134,16 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('modalState', modalState);
|
||||
if (modalState === 'INACTIVE') {
|
||||
refreshDocs(sortField, currentPage, rowsPerPage);
|
||||
}
|
||||
}, [modalState, sortField, currentPage, rowsPerPage]);
|
||||
}, [modalState]);
|
||||
|
||||
useEffect(() => {
|
||||
// undefine to prevent reset the sort order
|
||||
refreshDocs(undefined, 1, rowsPerPage);
|
||||
}, [searchTerm]);
|
||||
|
||||
return (
|
||||
<div className="mt-8">
|
||||
@@ -145,11 +158,18 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
type="text"
|
||||
id="document-search-input"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)} // Handle search input change
|
||||
onChange={(e) => {
|
||||
setSearchTerm(e.target.value);
|
||||
setCurrentPage(1);
|
||||
// refreshDocs(sortField, 1, rowsPerPage);
|
||||
// do not call refreshDocs here the state is async
|
||||
// so it will not have the updated value
|
||||
}} // Handle search input change
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="rounded-full w-40 bg-purple-30 px-4 py-3 text-white hover:bg-[#6F3FD1]"
|
||||
title="Add New Document"
|
||||
onClick={() => {
|
||||
setIsOnboarding(false); // Set onboarding flag if needed
|
||||
setModalState('ACTIVE'); // Open the upload modal
|
||||
@@ -161,94 +181,136 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
{loading ? (
|
||||
<SkeletonLoader count={1} />
|
||||
) : (
|
||||
<table className="table-default">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('settings.documents.name')}</th>
|
||||
<th>
|
||||
<div className="flex justify-center items-center">
|
||||
{t('settings.documents.date')}
|
||||
<img
|
||||
className="cursor-pointer"
|
||||
onClick={() => refreshDocs('date')}
|
||||
src={caretSort}
|
||||
alt="sort"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="flex justify-center items-center">
|
||||
{t('settings.documents.tokenUsage')}
|
||||
<img
|
||||
className="cursor-pointer"
|
||||
onClick={() => refreshDocs('tokens')}
|
||||
src={caretSort}
|
||||
alt="sort"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="flex justify-center items-center">
|
||||
{t('settings.documents.type')}
|
||||
</div>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!currentDocuments?.length && (
|
||||
<tr>
|
||||
<td colSpan={5} className="!p-4">
|
||||
{t('settings.documents.noData')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{Array.isArray(currentDocuments) &&
|
||||
currentDocuments.map((document, index) => (
|
||||
<tr key={index} className="text-nowrap font-normal">
|
||||
<td>{document.name}</td>
|
||||
<td>{document.date}</td>
|
||||
<td>
|
||||
{document.tokens ? formatTokens(+document.tokens) : ''}
|
||||
</td>
|
||||
<td>
|
||||
{document.type === 'remote' ? 'Pre-loaded' : 'Private'}
|
||||
</td>
|
||||
<td>
|
||||
<div className="min-w-[70px] flex flex-row items-end justify-end ml-auto">
|
||||
{document.type !== 'remote' && (
|
||||
<div className="flex flex-col">
|
||||
<div className="flex-grow">
|
||||
<div className="dark:border-silver/40 border-silver rounded-md border overflow-auto">
|
||||
<table className="min-w-full divide-y divide-silver dark:divide-silver/40 ">
|
||||
<thead>
|
||||
<tr className="text-nowrap">
|
||||
<th className="px-5 py-3 text-start text-sm font-medium text-gray-700 dark:text-gray-50 uppercase">
|
||||
{t('settings.documents.name')}
|
||||
</th>
|
||||
<th className="px-5 py-3 text-start text-sm font-medium text-gray-700 dark:text-gray-50 uppercase">
|
||||
<div className="flex justify-center items-center">
|
||||
{t('settings.documents.date')}
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
className="h-4 w-4 cursor-pointer opacity-60 hover:opacity-100"
|
||||
id={`img-${index}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDeleteDocument(index, document);
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
onClick={() => refreshDocs('date')}
|
||||
src={caretSort}
|
||||
alt="sort"
|
||||
/>
|
||||
)}
|
||||
{document.syncFrequency && (
|
||||
<div className="ml-2">
|
||||
<DropdownMenu
|
||||
name="Sync"
|
||||
options={syncOptions}
|
||||
onSelect={(value: string) => {
|
||||
handleManageSync(document, value);
|
||||
}}
|
||||
defaultValue={document.syncFrequency}
|
||||
icon={SyncIcon}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-5 py-2 text-center text-sm font-medium text-gray-700 dark:text-gray-50 uppercase">
|
||||
<div className="flex justify-center items-center">
|
||||
{t('settings.documents.tokenUsage')}
|
||||
<img
|
||||
className="cursor-pointer"
|
||||
onClick={() => refreshDocs('tokens')}
|
||||
src={caretSort}
|
||||
alt="sort"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
{/*}
|
||||
<th className="px-5 py-2 text-start text-sm font-medium text-gray-700 dark:text-gray-50 uppercase">
|
||||
<div className="flex justify-center items-center">
|
||||
{t('settings.documents.type')}
|
||||
</div>
|
||||
</th>
|
||||
*/}
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-2 text-start text-xs font-medium text-gray-700 dark:text-gray-50 uppercase"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-neutral-700">
|
||||
{!currentDocuments?.length && (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={5}
|
||||
className="!p-4 text-gray-800 dark:text-neutral-200 text-center"
|
||||
>
|
||||
{t('settings.documents.noData')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{Array.isArray(currentDocuments) &&
|
||||
currentDocuments.map((document, index) => (
|
||||
<tr key={index} className="text-nowrap font-normal">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-left text-sm font-medium text-gray-800 dark:text-neutral-200">
|
||||
{document.name}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-center text-sm font-medium text-gray-800 dark:text-neutral-200">
|
||||
{document.date}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-center text-sm font-medium text-gray-800 dark:text-neutral-200">
|
||||
{document.tokens
|
||||
? formatTokens(+document.tokens)
|
||||
: ''}
|
||||
</td>
|
||||
{/*}
|
||||
<td className="px-6 py-4 whitespace-nowrap text-center text-sm font-medium text-gray-800 dark:text-neutral-200">
|
||||
{document.type === 'remote'
|
||||
? 'Pre-loaded'
|
||||
: 'Private'}
|
||||
</td>
|
||||
*/}
|
||||
<td className="px-6 py-4 whitespace-nowrap text-left text-sm font-medium flex">
|
||||
<div className="min-w-[150px] flex flex-row items-center ml-auto gap-10">
|
||||
{document.type !== 'remote' && (
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
className="h-4 w-4 cursor-pointer opacity-60 hover:opacity-100"
|
||||
id={`img-${index}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDeleteDocument(index, document);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{document.syncFrequency && (
|
||||
<div className="ml-2">
|
||||
<DropdownMenu
|
||||
name="Sync"
|
||||
options={syncOptions}
|
||||
onSelect={(value: string) => {
|
||||
handleManageSync(document, value);
|
||||
}}
|
||||
defaultValue={document.syncFrequency}
|
||||
icon={SyncIcon}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* outside scrollable area */}
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
rowsPerPage={rowsPerPage}
|
||||
onPageChange={(page) => {
|
||||
setCurrentPage(page);
|
||||
refreshDocs(undefined, page, rowsPerPage);
|
||||
}}
|
||||
onRowsPerPageChange={(rows) => {
|
||||
setRowsPerPage(rows);
|
||||
setCurrentPage(1);
|
||||
refreshDocs(undefined, 1, rows);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Conditionally render the Upload modal based on modalState */}
|
||||
{modalState === 'ACTIVE' && (
|
||||
<div className="fixed top-0 left-0 w-screen h-screen z-50 flex items-center justify-center bg-transparent">
|
||||
@@ -263,25 +325,6 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Pagination component with props:
|
||||
# Note: Every time the page changes,
|
||||
the refreshDocs function is called with the updated page number and rows per page.
|
||||
and reset cursor paginated query parameter to undefined.
|
||||
*/}
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
rowsPerPage={rowsPerPage}
|
||||
onPageChange={(page) => {
|
||||
setCurrentPage(page);
|
||||
refreshDocs(sortField, page, rowsPerPage);
|
||||
}}
|
||||
onRowsPerPageChange={(rows) => {
|
||||
setRowsPerPage(rows);
|
||||
setCurrentPage(1);
|
||||
refreshDocs(sortField, 1, rows);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user