@@ -3,11 +3,19 @@ import { styled } from '@mui/material/styles';
33
44import { AppContext } from "../context/AppContext" ;
55
6- import { Button , DialogActions , DialogContent , DialogContentText , DialogTitle , Typography } from "@mui/material" ;
6+ import {
7+ Button ,
8+ DialogActions ,
9+ DialogContent ,
10+ DialogContentText ,
11+ DialogTitle ,
12+ TextField ,
13+ Typography
14+ } from "@mui/material" ;
715import SkillCategoryCard from "../components/skill-category-card/SkillCategoryCard" ;
816
917import "./SkillCategoriesPage.css" ;
10- import { selectCsrfToken } from "../context/selectors" ;
18+ import { selectCsrfToken , selectOrderedSkills } from "../context/selectors" ;
1119import {
1220 createSkillCategory ,
1321 deleteSkillCategory ,
@@ -16,6 +24,9 @@ import {
1624import SkillCategoryNewDialog from "../components/skill-category-new-dialog/SkillCategoryNewDialog" ;
1725import { UPDATE_TOAST } from "../context/actions" ;
1826import Dialog from "@mui/material/Dialog" ;
27+ import InputAdornment from "@mui/material/InputAdornment" ;
28+ import { Search } from "@mui/icons-material" ;
29+ import Autocomplete from "@mui/material/Autocomplete" ;
1930
2031const PREFIX = 'SkillCategoriesPage' ;
2132const classes = {
@@ -40,10 +51,13 @@ const Root = styled('div')({
4051const SkillCategoriesPage = ( ) => {
4152 const { state, dispatch } = useContext ( AppContext ) ;
4253 const csrf = selectCsrfToken ( state ) ;
54+ const skills = selectOrderedSkills ( state ) ;
4355
4456 const [ skillCategories , setSkillCategories ] = useState ( [ ] ) ;
4557 const [ dialogOpen , setDialogOpen ] = useState ( false ) ;
4658 const [ categoryToDelete , setCategoryToDelete ] = useState ( null ) ;
59+ const [ query , setQuery ] = useState ( "" ) ;
60+ const [ skillFilter , setSkillFilter ] = useState ( null ) ;
4761
4862 const retrieveCategories = useCallback ( async ( ) => {
4963 if ( csrf ) {
@@ -96,18 +110,73 @@ const SkillCategoriesPage = () => {
96110 }
97111 }
98112
113+ const getFilteredCategories = useCallback ( ( ) => {
114+ if ( skillCategories ) {
115+ return skillCategories . filter ( category => {
116+ let nameMatches = true ;
117+ if ( query ) {
118+ const sanitizedQuery = query . toLowerCase ( ) . trim ( ) ;
119+ nameMatches = category . name . toLowerCase ( ) . includes ( sanitizedQuery ) ;
120+ }
121+
122+ let skillMatches = true ;
123+ if ( skillFilter ) {
124+ skillMatches = category . skills . find ( skill => skill . name === skillFilter . name ) ;
125+ }
126+
127+ return nameMatches && skillMatches ;
128+ } ) ;
129+ }
130+
131+ return [ ] ;
132+ } , [ skillCategories , query , skillFilter ] ) ;
133+
99134 return (
100135 < Root className = { classes . root } >
101136 < div className = "skill-categories-header" >
102137 < Typography variant = "h4" > Skill Categories</ Typography >
103- < Button
104- variant = "contained"
105- onClick = { ( ) => setDialogOpen ( true ) }
106- >
107- New Category
108- </ Button >
138+ < div className = "skill-categories-actions" >
139+ < TextField
140+ style = { { minWidth : "200px" } }
141+ label = "Search"
142+ fullWidth
143+ placeholder = "Category name"
144+ variant = "outlined"
145+ size = "small"
146+ value = { query }
147+ onChange = { ( event ) => setQuery ( event . target . value ) }
148+ InputProps = { {
149+ endAdornment : < InputAdornment position = "end" color = "gray" > < Search /> </ InputAdornment >
150+ } }
151+ />
152+ < Autocomplete
153+ renderInput = { ( params ) => (
154+ < TextField
155+ { ...params }
156+ style = { { minWidth : "200px" } }
157+ label = "Filter by Skill"
158+ variant = "outlined"
159+ size = "small"
160+ placeholder = "Skill name"
161+ fullWidth
162+ />
163+ ) }
164+ options = { skills }
165+ getOptionLabel = { ( option ) => option . name }
166+ filterSelectedOptions
167+ value = { skillFilter }
168+ onChange = { ( _ , newValue ) => setSkillFilter ( newValue ) }
169+ />
170+ < Button
171+ style = { { width : "300px" } }
172+ variant = "contained"
173+ onClick = { ( ) => setDialogOpen ( true ) }
174+ >
175+ New Category
176+ </ Button >
177+ </ div >
109178 </ div >
110- { skillCategories . map ( category =>
179+ { getFilteredCategories ( ) . map ( category =>
111180 < SkillCategoryCard
112181 key = { category . id }
113182 id = { category . id }
0 commit comments