mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 03:40:14 +01:00
🔐 feat: Granular Role-based Permissions + Entra ID Group Discovery (#7804)
WIP: pre-granular-permissions commit
feat: Add category and support contact fields to Agent schema and UI components
Revert "feat: Add category and support contact fields to Agent schema and UI components"
This reverts commit c43a52b4c9.
Fix: Update import for renderHook in useAgentCategories.spec.tsx
fix: Update icon rendering in AgentCategoryDisplay tests to use empty spans
refactor: Improve category synchronization logic and clean up AgentConfig component
refactor: Remove unused UI flow translations from translation.json
feat: agent marketplace features
🔐 feat: Granular Role-based Permissions + Entra ID Group Discovery (#7804)
This commit is contained in:
parent
e42c129123
commit
5b4a2dcf9c
147 changed files with 17564 additions and 645 deletions
109
client/src/components/SidePanel/Agents/SearchBar.tsx
Normal file
109
client/src/components/SidePanel/Agents/SearchBar.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Search, X } from 'lucide-react';
|
||||
import { Input } from '@librechat/client';
|
||||
import { useDebounce, useLocalize } from '~/hooks';
|
||||
|
||||
/**
|
||||
* Props for the SearchBar component
|
||||
*/
|
||||
interface SearchBarProps {
|
||||
/** Current search query value */
|
||||
value: string;
|
||||
/** Callback fired when the search query changes */
|
||||
onSearch: (query: string) => void;
|
||||
/** Additional CSS classes */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SearchBar - Component for searching agents with debounced input
|
||||
*
|
||||
* Provides a search input with clear button and debounced search functionality.
|
||||
* Includes proper ARIA attributes for accessibility and visual indicators.
|
||||
* Uses 300ms debounce delay to prevent excessive API calls during typing.
|
||||
*/
|
||||
const SearchBar: React.FC<SearchBarProps> = ({ value, onSearch, className = '' }) => {
|
||||
const localize = useLocalize();
|
||||
const [searchTerm, setSearchTerm] = useState(value);
|
||||
|
||||
// Debounced search value (300ms delay)
|
||||
const debouncedSearchTerm = useDebounce(searchTerm, 300);
|
||||
|
||||
// Update internal state when props change
|
||||
useEffect(() => {
|
||||
setSearchTerm(value);
|
||||
}, [value]);
|
||||
|
||||
// Trigger search when debounced value changes
|
||||
useEffect(() => {
|
||||
// Only trigger search if the debounced value matches current searchTerm
|
||||
// This prevents stale debounced values from triggering after clear
|
||||
if (debouncedSearchTerm !== value && debouncedSearchTerm === searchTerm) {
|
||||
onSearch(debouncedSearchTerm);
|
||||
}
|
||||
}, [debouncedSearchTerm, onSearch, value, searchTerm]);
|
||||
|
||||
/**
|
||||
* Handle search input changes
|
||||
*
|
||||
* @param e - Input change event
|
||||
*/
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchTerm(e.target.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the search input and reset results
|
||||
*/
|
||||
const handleClear = useCallback(() => {
|
||||
// Immediately call parent onSearch to clear the URL parameter
|
||||
onSearch('');
|
||||
// Also clear local state
|
||||
setSearchTerm('');
|
||||
}, [onSearch]);
|
||||
|
||||
return (
|
||||
<div className={`relative w-full max-w-4xl ${className}`} role="search">
|
||||
<label htmlFor="agent-search" className="sr-only">
|
||||
{localize('com_agents_search_instructions')}
|
||||
</label>
|
||||
<Input
|
||||
id="agent-search"
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
onChange={handleChange}
|
||||
placeholder={localize('com_agents_search_placeholder')}
|
||||
className="h-14 rounded-2xl border-2 border-gray-200 bg-white pl-12 pr-12 text-lg text-gray-900 shadow-lg placeholder:text-gray-500 focus:border-gray-300 focus:ring-0 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:placeholder:text-gray-400 dark:focus:border-gray-500"
|
||||
aria-label={localize('com_agents_search_aria')}
|
||||
aria-describedby="search-instructions search-results-count"
|
||||
autoComplete="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
|
||||
{/* Search icon with proper accessibility */}
|
||||
<div className="absolute inset-y-0 left-0 flex items-center pl-4" aria-hidden="true">
|
||||
<Search className="h-6 w-6 text-gray-400" />
|
||||
</div>
|
||||
|
||||
{/* Hidden instructions for screen readers */}
|
||||
<div id="search-instructions" className="sr-only">
|
||||
{localize('com_agents_search_instructions')}
|
||||
</div>
|
||||
|
||||
{/* Show clear button only when search has value - Google style */}
|
||||
{searchTerm && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClear}
|
||||
className="group absolute right-3 top-1/2 flex h-6 w-6 -translate-y-1/2 items-center justify-center rounded-full bg-gray-400 transition-colors duration-150 hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-gray-500 dark:hover:bg-gray-400"
|
||||
aria-label={localize('com_agents_clear_search')}
|
||||
title={localize('com_agents_clear_search')}
|
||||
>
|
||||
<X className="h-3 w-3 text-white group-hover:text-white" strokeWidth={2.5} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchBar;
|
||||
Loading…
Add table
Add a link
Reference in a new issue