@@ -16,7 +16,7 @@ function LessonList(props) {
1616 const [ tags , setTags ] = useState ( [ ] ) ;
1717 const [ inputValue , setInputValue ] = useState ( '' ) ;
1818 const [ deleteValue , setDeleteInputValue ] = useState ( '' ) ;
19- const [ filteredLessons , setFilteredLessons ] = useState ( lessons ) ;
19+ const [ filteredLessons , setFilteredLessons ] = useState ( lessons || [ ] ) ;
2020 const [ filterOption , setFilterOption ] = useState ( '1' ) ;
2121 const [ sortOption , setSortOption ] = useState ( '1' ) ;
2222 const [ availableTags , setAvailableTags ] = useState ( [ ] ) ;
@@ -27,6 +27,24 @@ function LessonList(props) {
2727 const [ showExportModal , setShowExportModal ] = useState ( false ) ;
2828 const [ isExporting , setIsExporting ] = useState ( false ) ;
2929
30+ // Load saved tags from localStorage on mount
31+ useEffect ( ( ) => {
32+ const savedTags = localStorage . getItem ( 'lessonListSelectedTags' ) ;
33+ if ( savedTags ) {
34+ try {
35+ const parsedTags = JSON . parse ( savedTags ) ;
36+ if ( Array . isArray ( parsedTags ) && parsedTags . length > 0 ) {
37+ // Remove duplicates when loading from localStorage
38+ const uniqueTags = [ ...new Set ( parsedTags ) ] ;
39+ setTags ( uniqueTags ) ;
40+ }
41+ } catch ( error ) {
42+ // If parsing fails, ignore and use empty array
43+ console . error ( 'Failed to parse saved tags:' , error ) ;
44+ }
45+ }
46+ } , [ ] ) ;
47+
3048 useEffect ( ( ) => {
3149 const fetchData = async ( ) => {
3250 try {
@@ -40,6 +58,15 @@ function LessonList(props) {
4058 fetchData ( ) ;
4159 } , [ dispatch ] ) ;
4260
61+ // Save tags to localStorage whenever they change
62+ useEffect ( ( ) => {
63+ if ( tags . length > 0 ) {
64+ localStorage . setItem ( 'lessonListSelectedTags' , JSON . stringify ( tags ) ) ;
65+ } else {
66+ localStorage . removeItem ( 'lessonListSelectedTags' ) ;
67+ }
68+ } , [ tags ] ) ;
69+
4370 useEffect ( ( ) => {
4471 if ( lessons ) {
4572 setFilteredLessons ( lessons ) ;
@@ -153,6 +180,14 @@ function LessonList(props) {
153180
154181 useEffect ( ( ) => {
155182 const handleClickOutside = event => {
183+ // Don't close dropdown if clicking on tag close button or tag container
184+ const buttonClose = event . target . closest ( 'button[aria-label*="Remove"]' ) ;
185+ const tagContainer = event . target . closest ( `.${ styles . tagContainer } ` ) ;
186+
187+ if ( buttonClose || tagContainer ) {
188+ return ;
189+ }
190+
156191 if ( ! event . target . closest ( '.tags-input-container' ) ) {
157192 setShowDropdown ( false ) ;
158193 setShowDeleteDropdown ( false ) ;
@@ -165,9 +200,16 @@ function LessonList(props) {
165200 } , [ ] ) ;
166201
167202 const addTag = tag => {
168- // Check if the tag already exists
169- if ( tags . indexOf ( tag ) === - 1 ) {
170- setTags ( prevTags => [ ...prevTags , tag ] ) ;
203+ // Check if the tag already exists (case-insensitive check)
204+ const tagLower = tag . toLowerCase ( ) ;
205+ const tagExists = tags . some ( existingTag => existingTag . toLowerCase ( ) === tagLower ) ;
206+
207+ if ( ! tagExists ) {
208+ setTags ( prevTags => {
209+ // Ensure no duplicates when adding
210+ const newTags = [ ...prevTags , tag ] ;
211+ return [ ...new Set ( newTags ) ] ;
212+ } ) ;
171213 }
172214 setInputValue ( '' ) ;
173215 } ;
@@ -190,14 +232,22 @@ function LessonList(props) {
190232 setConfirmModal ( true ) ;
191233 } ;
192234
193- const removeTag = index => {
194- const newTags = [ ...tags ] ;
195- newTags . splice ( index , 1 ) ;
196- setTags ( newTags ) ;
235+ const removeTag = tagToRemove => {
236+ if ( ! tagToRemove ) return ;
237+
238+ setTags ( prevTags => {
239+ // Filter out the tag to remove, using exact match
240+ const newTags = prevTags . filter ( tag => tag !== tagToRemove ) ;
241+ return newTags ;
242+ } ) ;
197243 } ;
198244
199245 useEffect ( ( ) => {
200246 const applyFiltersAndSort = ( ) => {
247+ if ( ! lessons || ! Array . isArray ( lessons ) || lessons . length === 0 ) {
248+ setFilteredLessons ( [ ] ) ;
249+ return ;
250+ }
201251 let filtered = [ ...lessons ] ;
202252
203253 // 1. Apply tag filtering
@@ -622,16 +672,52 @@ function LessonList(props) {
622672 </ div >
623673 ) }
624674 </ InputGroup >
625- < div className = { `${ styles . tagContainer } ` } >
626- { tags . map ( tag => (
627- < div key = { tag } className = { `${ styles . tag } ` } >
628- < span > { tag } </ span >
629- < Button className = { `${ styles . buttonClose } ` } onClick = { ( ) => removeTag ( tag ) } >
630- x
631- </ Button >
632- </ div >
633- ) ) }
634- </ div >
675+ { tags . length > 0 && (
676+ < div
677+ className = { `${ styles . tagContainer } ${ darkMode ? styles . tagContainerDark : '' } ` }
678+ >
679+ { tags . map ( ( tag , index ) => {
680+ const handleRemoveClick = e => {
681+ e . preventDefault ( ) ;
682+ e . stopPropagation ( ) ;
683+ removeTag ( tag ) ;
684+ } ;
685+
686+ return (
687+ < div
688+ key = { `filter-tag-${ tag } -${ index } ` }
689+ className = { `${ styles . tag } ${ darkMode ? styles . tagDark : '' } ` }
690+ >
691+ < span className = { darkMode ? styles . tagTextDark : '' } > { tag } </ span >
692+ < span
693+ role = "button"
694+ tabIndex = { 0 }
695+ className = { `${ styles . buttonClose } ${
696+ darkMode ? styles . buttonCloseDark : ''
697+ } `}
698+ onClick = { handleRemoveClick }
699+ onKeyDown = { e => {
700+ if ( e . key === 'Enter' || e . key === ' ' ) {
701+ handleRemoveClick ( e ) ;
702+ }
703+ } }
704+ aria-label = { `Remove ${ tag } tag` }
705+ style = { {
706+ pointerEvents : 'auto' ,
707+ zIndex : 100 ,
708+ cursor : 'pointer' ,
709+ display : 'inline-flex' ,
710+ alignItems : 'center' ,
711+ justifyContent : 'center' ,
712+ } }
713+ >
714+ ×
715+ </ span >
716+ </ div >
717+ ) ;
718+ } ) }
719+ </ div >
720+ ) }
635721 </ div >
636722
637723 < Form . Label > Delete Tags (Press enter to add a tag to delete): </ Form . Label >
@@ -664,20 +750,42 @@ function LessonList(props) {
664750 </ div >
665751 ) }
666752 < div className = { `${ styles . tagContainer } ` } >
667- { tagsToDelete . map ( tag => (
668- < div key = { tag } className = { `${ styles . tag } ` } >
669- < span > { tag } </ span >
670- < Button
671- className = { `${ styles . buttonClose } ` }
672- onClick = { ( ) => {
673- const newTags = tagsToDelete . filter ( ( _ , i ) => i !== tag ) ;
674- setTagsToDelete ( newTags ) ;
675- } }
676- >
677- x
678- </ Button >
679- </ div >
680- ) ) }
753+ { tagsToDelete . map ( ( tag , index ) => {
754+ const handleRemoveDeleteTag = e => {
755+ e . preventDefault ( ) ;
756+ e . stopPropagation ( ) ;
757+ const newTags = tagsToDelete . filter ( t => t !== tag ) ;
758+ setTagsToDelete ( newTags ) ;
759+ } ;
760+
761+ return (
762+ < div key = { `delete-tag-${ tag } -${ index } ` } className = { `${ styles . tag } ` } >
763+ < span > { tag } </ span >
764+ < span
765+ role = "button"
766+ tabIndex = { 0 }
767+ className = { `${ styles . buttonClose } ` }
768+ onClick = { handleRemoveDeleteTag }
769+ onKeyDown = { e => {
770+ if ( e . key === 'Enter' || e . key === ' ' ) {
771+ handleRemoveDeleteTag ( e ) ;
772+ }
773+ } }
774+ aria-label = { `Remove ${ tag } from delete list` }
775+ style = { {
776+ pointerEvents : 'auto' ,
777+ zIndex : 100 ,
778+ cursor : 'pointer' ,
779+ display : 'inline-flex' ,
780+ alignItems : 'center' ,
781+ justifyContent : 'center' ,
782+ } }
783+ >
784+ ×
785+ </ span >
786+ </ div >
787+ ) ;
788+ } ) }
681789 </ div >
682790 </ div >
683791 { tagsToDelete . length > 0 && (
@@ -720,7 +828,7 @@ function LessonList(props) {
720828const mapStateToProps = state => {
721829 return {
722830 lessons : state . lessons . lessons ,
723- darkMode : state . theme . darkMode ,
831+ darkMode : state . theme ? .darkMode || false ,
724832 } ;
725833} ;
726834
0 commit comments