Merge pull request #2347 from ManishMadan2882/main

Minor frontend updates
This commit is contained in:
Alex authored and GitHub committed 2026-04-03 18:17:27 +01:00
commit c3f01c6619
4 files changed
+83 -18

No files matched your search

+19 -5
View File
@@ -78,6 +78,7 @@ function Dropdown<T extends DropdownOption>({
const searchRef = useRef<HTMLInputElement>(null);
const [open, setOpen] = useState(false);
const [query, setQuery] = useState('');
const [dropUp, setDropUp] = useState(false);
const radius = rounded === '3xl' ? 'rounded-3xl' : 'rounded-xl';
const radiusTop = rounded === '3xl' ? 'rounded-t-3xl' : 'rounded-t-xl';
@@ -90,14 +91,23 @@ function Dropdown<T extends DropdownOption>({
setQuery('');
}
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
document.addEventListener('mousedown', handler, true);
return () => document.removeEventListener('mousedown', handler, true);
}, []);
useEffect(() => {
if (open && searchable && searchRef.current) searchRef.current.focus();
}, [open, searchable]);
const handleToggle = () => {
if (!open && ref.current) {
const rect = ref.current.getBoundingClientRect();
const spaceBelow = window.innerHeight - rect.bottom;
setDropUp(spaceBelow < 220);
}
setOpen((v) => !v);
};
const filtered = useMemo(() => {
if (!searchable || !query.trim()) return options;
const q = query.toLowerCase();
@@ -110,8 +120,8 @@ function Dropdown<T extends DropdownOption>({
<div className={`relative ${size}`} ref={ref}>
<button
type="button"
onClick={() => setOpen((v) => !v)}
className={`border-border bg-card text-foreground flex w-full cursor-pointer items-center justify-between border px-5 py-3 ${open ? radiusTop : radius}`}
onClick={handleToggle}
className={`border-border bg-card text-foreground flex w-full cursor-pointer items-center justify-between border px-5 py-3 ${open ? (dropUp ? radiusBottom : radiusTop) : radius}`}
>
<span
className={`truncate ${contentSize} ${displayValue ? '' : 'text-muted-foreground'}`}
@@ -125,7 +135,11 @@ function Dropdown<T extends DropdownOption>({
{open && (
<div
className={`border-border bg-card absolute inset-x-0 z-20 -mt-px overflow-hidden border border-t-0 shadow-lg ${radiusBottom}`}
className={`border-border bg-card absolute inset-x-0 z-20 overflow-hidden border shadow-lg ${
dropUp
? `bottom-full -mt-px border-b-0 ${radiusTop}`
: `-mt-px border-t-0 ${radiusBottom}`
}`}
>
{searchable && (
<div className="flex items-center px-3 py-2">
+54 -1
View File
@@ -10,7 +10,9 @@ interface SkeletonLoaderProps {
| 'chatbot'
| 'dropdown'
| 'chunkCards'
| 'sourceCards';
| 'sourceCards'
| 'toolCards'
| 'addToolCards';
}
const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
@@ -237,6 +239,55 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
</>
);
const renderAddToolCards = () => (
<>
{Array.from({ length: count }).map((_, idx) => (
<div
key={`add-tool-skel-${idx}`}
className="border-light-gainsboro dark:border-arsenic flex h-52 w-full animate-pulse flex-col justify-between rounded-2xl border p-6"
>
<div className="w-full">
<div className="flex w-full items-center justify-between px-1">
<div className="h-6 w-6 rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
<div className="mt-[9px] space-y-2 px-1">
<div className="h-4 w-2/3 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-3 w-full rounded bg-gray-200 dark:bg-gray-700"></div>
<div className="h-3 w-5/6 rounded bg-gray-200 dark:bg-gray-700"></div>
<div className="h-3 w-3/4 rounded bg-gray-200 dark:bg-gray-700"></div>
</div>
</div>
</div>
))}
</>
);
const renderToolCards = () => (
<>
{Array.from({ length: count }).map((_, idx) => (
<div
key={`tool-skel-${idx}`}
className="bg-muted flex h-52 w-[300px] animate-pulse flex-col justify-between rounded-2xl p-6"
>
<div className="w-full">
<div className="flex items-center gap-2 px-1">
<div className="h-6 w-6 rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
<div className="mt-[9px] space-y-2 px-1">
<div className="h-4 w-2/3 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-3 w-full rounded bg-gray-200 dark:bg-gray-700"></div>
<div className="h-3 w-5/6 rounded bg-gray-200 dark:bg-gray-700"></div>
<div className="h-3 w-3/4 rounded bg-gray-200 dark:bg-gray-700"></div>
</div>
</div>
<div className="flex justify-end">
<div className="h-5 w-9 rounded-full bg-gray-300 dark:bg-gray-600"></div>
</div>
</div>
))}
</>
);
const componentMap = {
fileTable: renderTable,
chatbot: renderChatbot,
@@ -246,6 +297,8 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
analysis: renderAnalysis,
chunkCards: renderChunkCards,
sourceCards: renderSourceCards,
toolCards: renderToolCards,
addToolCards: renderAddToolCards,
};
const render = componentMap[component] || componentMap.default;
+5 -5
View File
@@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import userService from '../api/services/userService';
import Spinner from '../components/Spinner';
import { useOutsideAlerter } from '../hooks';
import SkeletonLoader from '../components/SkeletonLoader';
import { useLoaderState, useOutsideAlerter } from '../hooks';
import { ActiveState } from '../models/misc';
import { selectToken } from '../preferences/preferenceSlice';
import ConfigToolModal from './ConfigToolModal';
@@ -37,7 +37,7 @@ export default function AddToolModal({
React.useState<ActiveState>('INACTIVE');
const [mcpModalState, setMcpModalState] =
React.useState<ActiveState>('INACTIVE');
const [loading, setLoading] = React.useState(false);
const [loading, setLoading] = useLoaderState(false);
useOutsideAlerter(modalRef, () => {
if (modalState === 'ACTIVE') {
@@ -121,8 +121,8 @@ export default function AddToolModal({
</h2>
<div className="mt-5 h-[73vh] overflow-auto px-3 py-px">
{loading ? (
<div className="flex h-full items-center justify-center">
<Spinner />
<div className="grid auto-rows-fr grid-cols-1 gap-4 pb-2 sm:grid-cols-2 lg:grid-cols-3">
<SkeletonLoader component="addToolCards" count={6} />
</div>
) : (
<div className="grid auto-rows-fr grid-cols-1 gap-4 pb-2 sm:grid-cols-2 lg:grid-cols-3">
+5 -7
View File
@@ -10,9 +10,9 @@ import NoFilesIcon from '../assets/no-files.svg';
import SearchIcon from '../assets/search.svg';
import ThreeDotsIcon from '../assets/three-dots.svg';
import ContextMenu, { MenuOption } from '../components/ContextMenu';
import Spinner from '../components/Spinner';
import SkeletonLoader from '../components/SkeletonLoader';
import ToggleSwitch from '../components/ToggleSwitch';
import { useDarkTheme } from '../hooks';
import { useDarkTheme, useLoaderState } from '../hooks';
import AddToolModal from '../modals/AddToolModal';
import ConfirmationModal from '../modals/ConfirmationModal';
import MCPServerModal from '../modals/MCPServerModal';
@@ -33,7 +33,7 @@ export default function Tools() {
const [selectedTool, setSelectedTool] = React.useState<
UserToolType | APIToolType | null
>(null);
const [loading, setLoading] = React.useState(false);
const [loading, setLoading] = useLoaderState(false);
const [activeMenuId, setActiveMenuId] = React.useState<string | null>(null);
const menuRefs = React.useRef<{
[key: string]: React.RefObject<HTMLDivElement | null>;
@@ -242,10 +242,8 @@ export default function Tools() {
</div>
<div className="border-border dark:border-border mt-5 mb-8 border-b" />
{loading ? (
<div className="grid grid-cols-2 gap-6 lg:grid-cols-3">
<div className="col-span-2 mt-24 flex h-32 items-center justify-center lg:col-span-3">
<Spinner />
</div>
<div className="flex flex-wrap justify-center gap-4 sm:justify-start">
<SkeletonLoader component="toolCards" count={6} />
</div>
) : (
<div className="flex flex-wrap justify-center gap-4 sm:justify-start">