From f2ae3a51fa98ff6ae233569842869f81798b1a14 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Sun, 8 Feb 2026 18:43:16 -0500 Subject: [PATCH] fix: resolve all 15 frontend ESLint errors for green CI - Replace setState-in-effect with state-based tracking (AdminLayout, EditCategoryModal) - Convert inline SortIcon component to getSortIcon function (TreeTableView) - Remove unused catch parameters (CreateCategoryModal, EditCategoryModal) - Replace `any` types with proper types (SessionFilters, AdminCategoriesPage, SessionHistoryPage) - Fix unused destructuring variable (StepRatingModal) - Fix constant binary expression in test (utils.test.ts) Co-Authored-By: Claude Opus 4.6 --- frontend/src/components/admin/AdminLayout.tsx | 12 +++++++---- .../components/admin/CreateCategoryModal.tsx | 2 +- .../components/admin/EditCategoryModal.tsx | 21 +++++++++++-------- .../src/components/library/TreeTableView.tsx | 12 +++++------ .../src/components/session/SessionFilters.tsx | 7 ++++--- .../components/session/StepRatingModal.tsx | 2 +- frontend/src/lib/utils.test.ts | 3 ++- frontend/src/pages/AdminCategoriesPage.tsx | 2 +- frontend/src/pages/SessionHistoryPage.tsx | 2 +- 9 files changed, 36 insertions(+), 27 deletions(-) diff --git a/frontend/src/components/admin/AdminLayout.tsx b/frontend/src/components/admin/AdminLayout.tsx index 3318d639..210c6929 100644 --- a/frontend/src/components/admin/AdminLayout.tsx +++ b/frontend/src/components/admin/AdminLayout.tsx @@ -5,12 +5,16 @@ import { AdminSidebar } from './AdminSidebar' export function AdminLayout() { const [mobileOpen, setMobileOpen] = useState(false) + const [prevPathname, setPrevPathname] = useState('') const location = useLocation() - // Close on route change - useEffect(() => { - setMobileOpen(false) - }, [location.pathname]) + // Close on route change (state-based tracking, no effect needed) + if (prevPathname !== location.pathname) { + setPrevPathname(location.pathname) + if (mobileOpen) { + setMobileOpen(false) + } + } const handleKeyDown = useCallback((e: KeyboardEvent) => { if (e.key === 'Escape') setMobileOpen(false) diff --git a/frontend/src/components/admin/CreateCategoryModal.tsx b/frontend/src/components/admin/CreateCategoryModal.tsx index cdd16468..8cd5d934 100644 --- a/frontend/src/components/admin/CreateCategoryModal.tsx +++ b/frontend/src/components/admin/CreateCategoryModal.tsx @@ -43,7 +43,7 @@ export function CreateCategoryModal({ // Reset form on success setName('') setDescription('') - } catch (err) { + } catch { setError('Failed to create category') } } diff --git a/frontend/src/components/admin/EditCategoryModal.tsx b/frontend/src/components/admin/EditCategoryModal.tsx index 8321f513..9f763a08 100644 --- a/frontend/src/components/admin/EditCategoryModal.tsx +++ b/frontend/src/components/admin/EditCategoryModal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState } from 'react' import { X } from 'lucide-react' import { cn } from '@/lib/utils' import type { StepCategoryListItem } from '@/types' @@ -21,14 +21,17 @@ export function EditCategoryModal({ const [name, setName] = useState('') const [description, setDescription] = useState('') const [error, setError] = useState('') + const [prevCategoryId, setPrevCategoryId] = useState(null) - // Pre-populate form when category changes - useEffect(() => { - if (category) { - setName(category.name) - setDescription(category.description || '') - } - }, [category]) + // Pre-populate form when category changes (state-based tracking) + if (category && category.id !== prevCategoryId) { + setPrevCategoryId(category.id) + setName(category.name) + setDescription(category.description || '') + } + if (!category && prevCategoryId !== null) { + setPrevCategoryId(null) + } if (!isOpen || !category) return null @@ -51,7 +54,7 @@ export function EditCategoryModal({ name: name.trim(), description: description.trim() }) - } catch (err) { + } catch { setError('Failed to update category') } } diff --git a/frontend/src/components/library/TreeTableView.tsx b/frontend/src/components/library/TreeTableView.tsx index 2683a61e..2fd20994 100644 --- a/frontend/src/components/library/TreeTableView.tsx +++ b/frontend/src/components/library/TreeTableView.tsx @@ -54,7 +54,7 @@ export function TreeTableView({ onSortChange?.(apiSort) } - const SortIcon = ({ column }: { column: SortColumn }) => { + const getSortIcon = (column: SortColumn) => { if (sortColumn !== column) return null return sortDirection === 'asc' ? ( @@ -79,7 +79,7 @@ export function TreeTableView({ >
Name - + {getSortIcon('name')}
@@ -91,7 +91,7 @@ export function TreeTableView({ >
Category - + {getSortIcon('category')}
@@ -103,7 +103,7 @@ export function TreeTableView({ >
Ver. - + {getSortIcon('version')}
Uses - + {getSortIcon('usage')}
Updated - + {getSortIcon('updated')}
diff --git a/frontend/src/components/session/SessionFilters.tsx b/frontend/src/components/session/SessionFilters.tsx index 4d47f34e..389950e8 100644 --- a/frontend/src/components/session/SessionFilters.tsx +++ b/frontend/src/components/session/SessionFilters.tsx @@ -33,11 +33,12 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil const [showDatePicker, setShowDatePicker] = useState(false) const [localDateRange, setLocalDateRange] = useState(filters.dateRange) + const filtersDateRange = filters.dateRange useEffect(() => { - setLocalDateRange(filters.dateRange) - }, [filters.dateRange]) + setLocalDateRange(filtersDateRange) + }, [filtersDateRange]) - const handleFilterChange = (key: keyof SessionFilterState, value: any) => { + const handleFilterChange = (key: keyof SessionFilterState, value: SessionFilterState[keyof SessionFilterState]) => { onChange({ ...filters, [key]: value }) } diff --git a/frontend/src/components/session/StepRatingModal.tsx b/frontend/src/components/session/StepRatingModal.tsx index 1c08184a..3dc845a1 100644 --- a/frontend/src/components/session/StepRatingModal.tsx +++ b/frontend/src/components/session/StepRatingModal.tsx @@ -62,7 +62,7 @@ export function StepRatingModal({ const handleSubmit = async () => { // Filter out steps with no rating const ratingsToSubmit = new Map( - Array.from(ratings.entries()).filter(([_, data]) => data.rating > 0) + Array.from(ratings.entries()).filter(([, data]) => data.rating > 0) ) if (ratingsToSubmit.size === 0) { diff --git a/frontend/src/lib/utils.test.ts b/frontend/src/lib/utils.test.ts index 63994c95..e6cc8cb4 100644 --- a/frontend/src/lib/utils.test.ts +++ b/frontend/src/lib/utils.test.ts @@ -7,7 +7,8 @@ describe('cn', () => { }) it('handles conditional classes', () => { - expect(cn('base', false && 'hidden', 'visible')).toBe('base visible') + const isHidden = false + expect(cn('base', isHidden && 'hidden', 'visible')).toBe('base visible') }) it('deduplicates tailwind classes', () => { diff --git a/frontend/src/pages/AdminCategoriesPage.tsx b/frontend/src/pages/AdminCategoriesPage.tsx index a8673e7a..3e8691c4 100644 --- a/frontend/src/pages/AdminCategoriesPage.tsx +++ b/frontend/src/pages/AdminCategoriesPage.tsx @@ -13,7 +13,7 @@ import { toast } from '@/lib/toast' export function AdminCategoriesPage() { const [categories, setCategories] = useState([]) - const [allSteps, setAllSteps] = useState([]) + const [allSteps, setAllSteps] = useState<{ category_id?: string }[]>([]) const [isLoading, setIsLoading] = useState(true) const [showCreateModal, setShowCreateModal] = useState(false) const [showEditModal, setShowEditModal] = useState(false) diff --git a/frontend/src/pages/SessionHistoryPage.tsx b/frontend/src/pages/SessionHistoryPage.tsx index 9e5612db..b3366746 100644 --- a/frontend/src/pages/SessionHistoryPage.tsx +++ b/frontend/src/pages/SessionHistoryPage.tsx @@ -75,7 +75,7 @@ export function SessionHistoryPage() { const loadSessions = async () => { setIsLoading(true) try { - const params: any = {} + const params: Record = {} // Tab filter (all/active/completed) if (filter !== 'all') {