From 7d49fb490101b38d72d6cd9d4a5b61d8403f246f Mon Sep 17 00:00:00 2001 From: Kaic Bento Date: Wed, 7 Jan 2026 16:19:50 -0300 Subject: [PATCH 1/6] feat(ui): add select all button to category headers --- .../SoftwareSelector/CategorySection.jsx | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/src/components/SoftwareSelector/CategorySection.jsx b/src/components/SoftwareSelector/CategorySection.jsx index e5ab365..a830696 100644 --- a/src/components/SoftwareSelector/CategorySection.jsx +++ b/src/components/SoftwareSelector/CategorySection.jsx @@ -1,38 +1,80 @@ import { useState } from 'react'; import SoftwareCard from './SoftwareCard'; +import { useSelection } from '../../context/SelectionContext'; const CategorySection = ({ category, software }) => { const [expanded, setExpanded] = useState(true); + const { isAllCategorySelected, selectAllInCategory, deselectAllInCategory } = useSelection(); + + const allSelected = isAllCategorySelected(category.id); + + const handleSelectAll = (e) => { + e.stopPropagation(); + if (allSelected) { + deselectAllInCategory(category.id); + } else { + selectAllInCategory(category.id); + } + }; return (
-
+
+ +
From 56c8e38b5b905cf7b671b9e1b9a1d2909984d367 Mon Sep 17 00:00:00 2001 From: Kaic Bento Date: Wed, 7 Jan 2026 17:23:50 -0300 Subject: [PATCH 2/6] feat(core): add SearchContext for global state management --- src/context/SearchContext.jsx | 21 +++++++++++++++++++++ src/main.jsx | 5 ++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/context/SearchContext.jsx diff --git a/src/context/SearchContext.jsx b/src/context/SearchContext.jsx new file mode 100644 index 0000000..cca5abc --- /dev/null +++ b/src/context/SearchContext.jsx @@ -0,0 +1,21 @@ +import { createContext, useContext, useState } from 'react'; + +const SearchContext = createContext(); + +export function SearchProvider({ children }) { + const [searchTerm, setSearchTerm] = useState(''); + + return ( + + {children} + + ); +} + +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( - + + + , From eaafe48c6158f1cee16fabbc66855e4a3de141ab Mon Sep 17 00:00:00 2001 From: Kaic Bento Date: Wed, 7 Jan 2026 17:23:55 -0300 Subject: [PATCH 3/6] feat(ui): implement search bar in ActionBar with Win98 styling --- src/components/ActionBar/ActionBar.jsx | 36 ++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/components/ActionBar/ActionBar.jsx b/src/components/ActionBar/ActionBar.jsx index e3eec26..a378f83 100644 --- a/src/components/ActionBar/ActionBar.jsx +++ b/src/components/ActionBar/ActionBar.jsx @@ -1,11 +1,14 @@ import { useSelection } from '../../context/SelectionContext'; import { useScriptGenerator } from '../../hooks/useScriptGenerator'; +import { useSearchContext } from '../../context/SearchContext'; import { useState } from 'react'; import WinDialog from '../Common/WinDialog'; +import { FaSearch } from '../Common/icons'; const ActionBar = () => { const { selectedSoftware, selectedConfigs, clearAll } = useSelection(); const { downloadScript } = useScriptGenerator(); + const { searchTerm, setSearchTerm } = useSearchContext(); const [showClearDialog, setShowClearDialog] = useState(false); const [showDownloadDialog, setShowDownloadDialog] = useState(false); @@ -30,7 +33,7 @@ const ActionBar = () => {
{/* Left: Selection count */} -
+
{totalSelected} items selected @@ -39,8 +42,37 @@ const ActionBar = () => {
+ {/* Center: Search Bar */} +
+
+ + setSearchTerm(e.target.value)} + placeholder="Search catalog..." + style={{ + border: 'none', + outline: 'none', + fontSize: '12px', + fontFamily: 'inherit', + width: '100%', + backgroundColor: 'transparent', + color: 'var(--win95-black)' + }} + /> +
+
+ {/* Right: Action buttons */} -
+
+ +
diff --git a/src/context/SelectionContext.jsx b/src/context/SelectionContext.jsx index 44fa2f0..7ee947f 100644 --- a/src/context/SelectionContext.jsx +++ b/src/context/SelectionContext.jsx @@ -1,5 +1,6 @@ import { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react'; import { softwareCatalog } from '../data/software-catalog'; +import { configurations } from '../data/configurations'; import { getCategoryItemIds } from '../utils/catalogHelpers'; import { STORAGE_KEYS } from '../constants'; @@ -73,9 +74,31 @@ export function SelectionProvider({ children }) { // Check if all items in category are selected const isAllCategorySelected = useCallback((categoryId) => { const categoryItems = getCategoryItemIds(softwareCatalog, categoryId); + if (categoryItems.length === 0) return false; return categoryItems.every((id) => selectedSoftware.includes(id)); }, [selectedSoftware]); + // Select all configurations in a category + const selectAllConfigsInCategory = useCallback((categoryId) => { + const categoryItems = getCategoryItemIds(configurations, categoryId); + setSelectedConfigs((prev) => [...new Set([...prev, ...categoryItems])]); + }, []); + + // Deselect all configurations in a category + const deselectAllConfigsInCategory = useCallback((categoryId) => { + const categoryItems = getCategoryItemIds(configurations, categoryId); + setSelectedConfigs((prev) => + prev.filter((id) => !categoryItems.includes(id)) + ); + }, []); + + // Check if all configurations in category are selected + const isAllConfigCategorySelected = useCallback((categoryId) => { + const categoryItems = getCategoryItemIds(configurations, categoryId); + if (categoryItems.length === 0) return false; + return categoryItems.every((id) => selectedConfigs.includes(id)); + }, [selectedConfigs]); + // Clear all selections const clearAll = useCallback(() => { setSelectedSoftware([]); @@ -100,6 +123,9 @@ export function SelectionProvider({ children }) { selectAllInCategory, deselectAllInCategory, isAllCategorySelected, + selectAllConfigsInCategory, + deselectAllConfigsInCategory, + isAllConfigCategorySelected, clearAll, clearSoftware, clearConfigs, @@ -111,6 +137,9 @@ export function SelectionProvider({ children }) { selectAllInCategory, deselectAllInCategory, isAllCategorySelected, + selectAllConfigsInCategory, + deselectAllConfigsInCategory, + isAllConfigCategorySelected, clearAll, clearSoftware, clearConfigs,