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 3 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
56 changes: 45 additions & 11 deletions frontend/src/settings/APIKeys.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';

import userService from '../api/services/userService';
import Trash from '../assets/trash.svg';
import CreateAPIKeyModal from '../modals/CreateAPIKeyModal';
import SaveAPIKeyModal from '../modals/SaveAPIKeyModal';
import { APIKeyData } from './types';
import Input from '../components/Input';

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 [isCreateModalOpen, setCreateModal] = useState(false);
const [isSaveKeyModalOpen, setSaveKeyModal] = useState(false);
const [newKey, setNewKey] = useState('');
const [apiKeys, setApiKeys] = useState<APIKeyData[]>([]);
const [searchTerm, setSearchTerm] = useState(''); // Added state for search term
const [filteredKeys, setFilteredKeys] = useState<APIKeyData[]>([]); // State for filtered API keys

const handleFetchKeys = async () => {
try {
Expand All @@ -22,6 +25,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);
}
Expand All @@ -37,8 +41,12 @@ export default function APIKeys() {
return response.json();
})
.then((data) => {
data.status === 'ok' &&
if (data.status === 'ok') {
setApiKeys((previous) => previous.filter((elem) => elem.id !== id));
setFilteredKeys((previous) =>
previous.filter((elem) => elem.id !== id),
);
}
})
.catch((error) => {
console.error(error);
Expand All @@ -62,6 +70,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 @@ -72,13 +81,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 Down Expand Up @@ -110,14 +143,14 @@ export default function APIKeys() {
</tr>
</thead>
<tbody>
{!apiKeys?.length && (
{!filteredKeys.length && (
<tr>
<td colSpan={4} className="!p-4">
{t('settings.apiKeys.noData')}
</td>
</tr>
)}
{apiKeys?.map((element, index) => (
{filteredKeys.map((element, index) => (
<tr key={index}>
<td>{element.name}</td>
<td>{element.source}</td>
Expand All @@ -126,7 +159,8 @@ export default function APIKeys() {
<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={() => handleDeleteKey(element.id)}
/>
Expand Down
58 changes: 53 additions & 5 deletions frontend/src/settings/Documents.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
Expand All @@ -6,9 +7,11 @@ import userService from '../api/services/userService';
import SyncIcon from '../assets/sync.svg';
import Trash from '../assets/trash.svg';
import DropdownMenu from '../components/DropdownMenu';
import { Doc, DocumentsProps } from '../models/misc';
import { Doc, DocumentsProps, ActiveState } from '../models/misc'; // Ensure ActiveState type is imported
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 @@ -33,6 +36,13 @@ 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 syncOptions = [
{ label: 'Never', value: 'never' },
{ label: 'Daily', value: 'daily' },
Expand All @@ -51,10 +61,38 @@ const Documents: React.FC<DocumentsProps> = ({
})
.catch((error) => console.error(error));
};

// 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>
<table className="table-default">
<thead>
<tr>
Expand All @@ -66,15 +104,15 @@ const Documents: React.FC<DocumentsProps> = ({
</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 @@ -90,7 +128,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 @@ -118,12 +156,22 @@ const Documents: React.FC<DocumentsProps> = ({
</tbody>
</table>
</div>
{/* Conditionally render the Upload modal based on modalState */}
{modalState === 'ACTIVE' && (
<Upload
modalState={modalState}
setModalState={setModalState}
isOnboarding={isOnboarding} // Pass the onboarding flag
/>
)}
</div>
</div>
);
};

Documents.propTypes = {
documents: PropTypes.array.isRequired,
handleDeleteDocument: PropTypes.func.isRequired,
};

export default Documents;
Loading