@@ -16,13 +19,24 @@ const ConfigurationSelector = () => {
{configCategories.map((category) => {
const categoryConfigs = getConfigsByCategory(category.id);
- if (categoryConfigs.length === 0) return null;
+
+ const filteredConfigs = categoryConfigs.filter((config) => {
+ if (!searchTerm) return true;
+ const searchLower = searchTerm.toLowerCase();
+ return (
+ config.name.toLowerCase().includes(searchLower) ||
+ (config.description && config.description.toLowerCase().includes(searchLower))
+ );
+ });
+
+ if (filteredConfigs.length === 0) return null;
return (
);
})}
diff --git a/src/components/SoftwareSelector/CategorySection.jsx b/src/components/SoftwareSelector/CategorySection.jsx
index a830696..08a7d8d 100644
--- a/src/components/SoftwareSelector/CategorySection.jsx
+++ b/src/components/SoftwareSelector/CategorySection.jsx
@@ -2,10 +2,19 @@ import { useState } from 'react';
import SoftwareCard from './SoftwareCard';
import { useSelection } from '../../context/SelectionContext';
-const CategorySection = ({ category, software }) => {
+const CategorySection = ({ category, software, isSearching }) => {
const [expanded, setExpanded] = useState(true);
+ const [prevIsSearching, setPrevIsSearching] = useState(isSearching);
const { isAllCategorySelected, selectAllInCategory, deselectAllInCategory } = useSelection();
+ // Derived state: Automatically expand when search becomes active
+ if (isSearching !== prevIsSearching) {
+ setPrevIsSearching(isSearching);
+ if (isSearching) {
+ setExpanded(true);
+ }
+ }
+
const allSelected = isAllCategorySelected(category.id);
const handleSelectAll = (e) => {
diff --git a/src/components/SoftwareSelector/SoftwareSelector.jsx b/src/components/SoftwareSelector/SoftwareSelector.jsx
index 32b1a01..1e261e0 100644
--- a/src/components/SoftwareSelector/SoftwareSelector.jsx
+++ b/src/components/SoftwareSelector/SoftwareSelector.jsx
@@ -1,10 +1,12 @@
import { categories } from '../../data/categories';
import { getSoftwareByCategory } from '../../data/software-catalog';
import CategorySection from './CategorySection';
+import { useSearchContext } from '../../context/SearchContext';
const SoftwareSelector = () => {
// Sort categories by order
const sortedCategories = [...categories].sort((a, b) => a.order - b.order);
+ const { searchTerm } = useSearchContext();
return (
@@ -19,13 +21,24 @@ const SoftwareSelector = () => {
{sortedCategories.map((category) => {
const categorySoftware = getSoftwareByCategory(category.id);
- if (categorySoftware.length === 0) return null;
+
+ const filteredSoftware = categorySoftware.filter((sw) => {
+ if (!searchTerm) return true;
+ const searchLower = searchTerm.toLowerCase();
+ return (
+ sw.name.toLowerCase().includes(searchLower) ||
+ (sw.description && sw.description.toLowerCase().includes(searchLower))
+ );
+ });
+
+ if (filteredSoftware.length === 0) return null;
return (
);
})}
diff --git a/src/context/SearchContext.jsx b/src/context/SearchContext.jsx
new file mode 100644
index 0000000..315b42f
--- /dev/null
+++ b/src/context/SearchContext.jsx
@@ -0,0 +1,22 @@
+import { createContext, useContext, useState } from 'react';
+
+const SearchContext = createContext();
+
+export function SearchProvider({ children }) {
+ const [searchTerm, setSearchTerm] = useState('');
+
+ return (
+
+ {children}
+
+ );
+}
+
+// eslint-disable-next-line react-refresh/only-export-components
+export function useSearchContext() {
+ const context = useContext(SearchContext);
+ if (!context) {
+ throw new Error('useSearchContext must be used within a SearchProvider');
+ }
+ return context;
+}
diff --git a/src/main.jsx b/src/main.jsx
index 914a3f8..dc09991 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -4,12 +4,15 @@ import './index.css'
import App from './App.jsx'
import { SelectionProvider } from './context/SelectionContext.jsx'
import { ThemeProvider } from './context/ThemeContext.jsx'
+import { SearchProvider } from './context/SearchContext.jsx'
createRoot(document.getElementById('root')).render(
-
+
+
+
,