Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Table redesign #1300

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/assets/caret-sort.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,24 @@ body.dark {
}

.table-default th {
@apply border-r border-silver dark:border-silver/40 p-4 w-[244px];
@apply p-4 w-[244px] font-normal text-gray-400; /* Remove border-r */
}

.table-default th:last-child {
@apply w-[auto] border-r-0;
@apply w-[auto];
}

.table-default td {
@apply border-r border-t border-silver dark:border-silver/40 px-4 py-2;
@apply border-t border-silver dark:border-silver/40 px-4 py-2; /* Remove border-r */
}

.table-default td:last-child {
@apply border-r-0;
@apply border-r-0; /* Ensure no right border on the last column */
}

}


/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */

/* Document
Expand Down
79 changes: 69 additions & 10 deletions frontend/src/settings/APIKeys.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';

import userService from '../api/services/userService';
Expand All @@ -7,6 +7,7 @@ import CreateAPIKeyModal from '../modals/CreateAPIKeyModal';
import SaveAPIKeyModal from '../modals/SaveAPIKeyModal';
import { APIKeyData } from './types';
import SkeletonLoader from '../components/SkeletonLoader';
import Input from '../components/Input';

export default function APIKeys() {
const { t } = useTranslation();
Expand All @@ -15,6 +16,9 @@ export default function APIKeys() {
const [newKey, setNewKey] = React.useState('');
const [apiKeys, setApiKeys] = React.useState<APIKeyData[]>([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState(''); // Added state for search term
const [filteredKeys, setFilteredKeys] = useState<APIKeyData[]>([]); // State for filtered API keys


const handleFetchKeys = async () => {
setLoading(true);
Expand All @@ -25,6 +29,7 @@ export default function APIKeys() {
}
const apiKeys = await response.json();
setApiKeys(apiKeys);
setFilteredKeys(apiKeys); // Initialize the filtered keys as the fetched ones
} catch (error) {
console.log(error);
} finally {
Expand All @@ -42,8 +47,13 @@ export default function APIKeys() {
return response.json();
})
.then((data) => {

data.success === true &&
setApiKeys((previous) => previous.filter((elem) => elem.id !== id));
setFilteredKeys((previous) =>
previous.filter((elem) => elem.id !== id),
);
}
})
.catch((error) => {
console.error(error);
Expand All @@ -67,6 +77,7 @@ export default function APIKeys() {
})
.then((data) => {
setApiKeys([...apiKeys, data]);
setFilteredKeys([...apiKeys, data]); // Update filtered keys too
setCreateModal(false);
setNewKey(data.key);
setSaveKeyModal(true);
Expand All @@ -77,14 +88,37 @@ export default function APIKeys() {
});
};

React.useEffect(() => {
useEffect(() => {
handleFetchKeys();
}, []);

// Filter API keys when the search term changes
useEffect(() => {
setFilteredKeys(
apiKeys.filter(
(key) =>
key.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
key.source?.toLowerCase().includes(searchTerm.toLowerCase()) ||
key.key.toLowerCase().includes(searchTerm.toLowerCase()),
),
);
}, [searchTerm, apiKeys]);

return (
<div className="mt-8">
<div className="flex flex-col max-w-[876px]">
<div className="flex justify-end">
<div className="flex justify-between">
<div className="p-1">
<Input
maxLength={256}
placeholder="Search..."
name="APIkey-search-input"
type="text"
id="apikey-search-input"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} // Update search term
/>
</div>
<button
onClick={() => setCreateModal(true)}
className="rounded-full bg-purple-30 px-4 py-3 text-white hover:bg-[#6F3FD1]"
Expand All @@ -106,16 +140,41 @@ export default function APIKeys() {
)}
<div className="mt-[27px] w-full">
<div className="w-full overflow-x-auto">
{loading ? (
{loading ? (
<SkeletonLoader count={1} component={'chatbot'} />
) : (
<table className="table-default">
<thead>
<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>
{!filteredKeys.length && (
<tr>
<th>{t('settings.apiKeys.name')}</th>
<th>{t('settings.apiKeys.sourceDoc')}</th>
<th>{t('settings.apiKeys.key')}</th>
<th></th>
<td colSpan={4} className="!p-4">
{t('settings.apiKeys.noData')}
</td>
</tr>
)}
{filteredKeys.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 opacity-60
hover:opacity-100"
id={`img-${index}`}
onClick={() => handleDeleteKey(element.id)}
/>
</td>
</tr>
</thead>
<tbody>
Expand Down
81 changes: 73 additions & 8 deletions frontend/src/settings/Documents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import { useDispatch } from 'react-redux';
import userService from '../api/services/userService';
import SyncIcon from '../assets/sync.svg';
import Trash from '../assets/trash.svg';
import caretSort from '../assets/caret-sort.svg';
import DropdownMenu from '../components/DropdownMenu';
import { Doc, DocumentsProps, ActiveState } from '../models/misc'; // Ensure ActiveState type is imported
import SkeletonLoader from '../components/SkeletonLoader';
import { Doc, DocumentsProps } from '../models/misc';
import { getDocs } from '../preferences/preferenceApi';
import { setSourceDocs } from '../preferences/preferenceSlice';
import Input from '../components/Input';
import Upload from '../upload/Upload'; // Import the Upload component

// Utility function to format numbers
const formatTokens = (tokens: number): string => {
Expand All @@ -35,7 +38,14 @@ const Documents: React.FC<DocumentsProps> = ({
}) => {
const { t } = useTranslation();
const dispatch = useDispatch();

// State for search input
const [searchTerm, setSearchTerm] = useState('');
// 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 syncOptions = [
{ label: 'Never', value: 'never' },
{ label: 'Daily', value: 'daily' },
Expand All @@ -59,33 +69,75 @@ const Documents: React.FC<DocumentsProps> = ({
});
};

// Filter documents based on the search term
const filteredDocuments = documents?.filter((document) =>
document.name.toLowerCase().includes(searchTerm.toLowerCase()),
);

return (
<div className="mt-8">
<div className="flex flex-col relative">
<div className="z-10 w-full overflow-x-auto">
<div className="my-3 flex justify-between items-center">
<div className="p-1">
<Input
maxLength={256}
placeholder="Search..."
name="Document-search-input"
type="text"
id="document-search-input"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} // Handle search input change
/>
</div>
<button
className="rounded-full w-40 bg-purple-30 px-4 py-3 text-white hover:bg-[#6F3FD1]"
onClick={() => {
setIsOnboarding(false); // Set onboarding flag if needed
setModalState('ACTIVE'); // Open the upload modal
}}
>
Add New
</button>
</div>
{loading ? (
<SkeletonLoader count={1} />
) : (
<table className="table-default">
<thead>
<tr>
<th>{t('settings.documents.name')}</th>
<th>{t('settings.documents.date')}</th>
<th>{t('settings.documents.tokenUsage')}</th>
<th>{t('settings.documents.type')}</th>
<th>
<div className="flex justify-center items-center">
{t('settings.documents.date')}
<img src={caretSort} alt="" />
</div>
</th>
<th>
<div className="flex justify-center items-center">
{t('settings.documents.tokenUsage')}
<img src={caretSort} alt="" />
</div>
</th>
<th>
<div className="flex justify-center items-center">
{t('settings.documents.type')}
<img src={caretSort} alt="" />
</div>
</th>
<th></th>
</tr>
</thead>
<tbody>
{!documents?.length && (
{!filteredDocuments?.length && (
<tr>
<td colSpan={5} className="!p-4">
{t('settings.documents.noData')}
</td>
</tr>
)}
{documents &&
documents.map((document, index) => (
{filteredDocuments &&
filteredDocuments.map((document, index) => (
<tr key={index}>
<td>{document.name}</td>
<td>{document.date}</td>
Expand All @@ -101,7 +153,7 @@ const Documents: React.FC<DocumentsProps> = ({
<img
src={Trash}
alt="Delete"
className="h-4 w-4 cursor-pointer hover:opacity-50"
className="h-4 w-4 cursor-pointer opacity-60 hover:opacity-100"
id={`img-${index}`}
onClick={(event) => {
event.stopPropagation();
Expand Down Expand Up @@ -130,6 +182,19 @@ const Documents: React.FC<DocumentsProps> = ({
</table>
)}
</div>
{/* 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">
<div className="w-full h-full bg-transparent flex flex-col items-center justify-center p-8">
{/* Your Upload component */}
<Upload
modalState={modalState}
setModalState={setModalState}
isOnboarding={isOnboarding}
/>
</div>
</div>
)}
</div>
</div>
);
Expand Down
Loading