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 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-08 18:43:16 -05:00
parent 6752a55ff8
commit f2ae3a51fa
9 changed files with 36 additions and 27 deletions

View File

@@ -5,12 +5,16 @@ import { AdminSidebar } from './AdminSidebar'
export function AdminLayout() { export function AdminLayout() {
const [mobileOpen, setMobileOpen] = useState(false) const [mobileOpen, setMobileOpen] = useState(false)
const [prevPathname, setPrevPathname] = useState('')
const location = useLocation() const location = useLocation()
// Close on route change // Close on route change (state-based tracking, no effect needed)
useEffect(() => { if (prevPathname !== location.pathname) {
setMobileOpen(false) setPrevPathname(location.pathname)
}, [location.pathname]) if (mobileOpen) {
setMobileOpen(false)
}
}
const handleKeyDown = useCallback((e: KeyboardEvent) => { const handleKeyDown = useCallback((e: KeyboardEvent) => {
if (e.key === 'Escape') setMobileOpen(false) if (e.key === 'Escape') setMobileOpen(false)

View File

@@ -43,7 +43,7 @@ export function CreateCategoryModal({
// Reset form on success // Reset form on success
setName('') setName('')
setDescription('') setDescription('')
} catch (err) { } catch {
setError('Failed to create category') setError('Failed to create category')
} }
} }

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react' import { useState } from 'react'
import { X } from 'lucide-react' import { X } from 'lucide-react'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import type { StepCategoryListItem } from '@/types' import type { StepCategoryListItem } from '@/types'
@@ -21,14 +21,17 @@ export function EditCategoryModal({
const [name, setName] = useState('') const [name, setName] = useState('')
const [description, setDescription] = useState('') const [description, setDescription] = useState('')
const [error, setError] = useState('') const [error, setError] = useState('')
const [prevCategoryId, setPrevCategoryId] = useState<string | null>(null)
// Pre-populate form when category changes // Pre-populate form when category changes (state-based tracking)
useEffect(() => { if (category && category.id !== prevCategoryId) {
if (category) { setPrevCategoryId(category.id)
setName(category.name) setName(category.name)
setDescription(category.description || '') setDescription(category.description || '')
} }
}, [category]) if (!category && prevCategoryId !== null) {
setPrevCategoryId(null)
}
if (!isOpen || !category) return null if (!isOpen || !category) return null
@@ -51,7 +54,7 @@ export function EditCategoryModal({
name: name.trim(), name: name.trim(),
description: description.trim() description: description.trim()
}) })
} catch (err) { } catch {
setError('Failed to update category') setError('Failed to update category')
} }
} }

View File

@@ -54,7 +54,7 @@ export function TreeTableView({
onSortChange?.(apiSort) onSortChange?.(apiSort)
} }
const SortIcon = ({ column }: { column: SortColumn }) => { const getSortIcon = (column: SortColumn) => {
if (sortColumn !== column) return null if (sortColumn !== column) return null
return sortDirection === 'asc' ? ( return sortDirection === 'asc' ? (
<ChevronUp className="h-3.5 w-3.5" /> <ChevronUp className="h-3.5 w-3.5" />
@@ -79,7 +79,7 @@ export function TreeTableView({
> >
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
Name Name
<SortIcon column="name" /> {getSortIcon('name')}
</div> </div>
</th> </th>
<th className="hidden md:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground"> <th className="hidden md:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground">
@@ -91,7 +91,7 @@ export function TreeTableView({
> >
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
Category Category
<SortIcon column="category" /> {getSortIcon('category')}
</div> </div>
</th> </th>
<th className="hidden xl:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground"> <th className="hidden xl:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground">
@@ -103,7 +103,7 @@ export function TreeTableView({
> >
<div className="flex items-center justify-center gap-1"> <div className="flex items-center justify-center gap-1">
Ver. Ver.
<SortIcon column="version" /> {getSortIcon('version')}
</div> </div>
</th> </th>
<th <th
@@ -112,7 +112,7 @@ export function TreeTableView({
> >
<div className="flex items-center justify-center gap-1"> <div className="flex items-center justify-center gap-1">
Uses Uses
<SortIcon column="usage" /> {getSortIcon('usage')}
</div> </div>
</th> </th>
<th <th
@@ -121,7 +121,7 @@ export function TreeTableView({
> >
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
Updated Updated
<SortIcon column="updated" /> {getSortIcon('updated')}
</div> </div>
</th> </th>
<th className="px-4 py-3 text-right text-sm font-medium text-muted-foreground"> <th className="px-4 py-3 text-right text-sm font-medium text-muted-foreground">

View File

@@ -33,11 +33,12 @@ export function SessionFilters({ filters, onChange, onClear, trees }: SessionFil
const [showDatePicker, setShowDatePicker] = useState(false) const [showDatePicker, setShowDatePicker] = useState(false)
const [localDateRange, setLocalDateRange] = useState<DateRange | undefined>(filters.dateRange) const [localDateRange, setLocalDateRange] = useState<DateRange | undefined>(filters.dateRange)
const filtersDateRange = filters.dateRange
useEffect(() => { useEffect(() => {
setLocalDateRange(filters.dateRange) setLocalDateRange(filtersDateRange)
}, [filters.dateRange]) }, [filtersDateRange])
const handleFilterChange = (key: keyof SessionFilterState, value: any) => { const handleFilterChange = (key: keyof SessionFilterState, value: SessionFilterState[keyof SessionFilterState]) => {
onChange({ ...filters, [key]: value }) onChange({ ...filters, [key]: value })
} }

View File

@@ -62,7 +62,7 @@ export function StepRatingModal({
const handleSubmit = async () => { const handleSubmit = async () => {
// Filter out steps with no rating // Filter out steps with no rating
const ratingsToSubmit = new Map( 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) { if (ratingsToSubmit.size === 0) {

View File

@@ -7,7 +7,8 @@ describe('cn', () => {
}) })
it('handles conditional classes', () => { 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', () => { it('deduplicates tailwind classes', () => {

View File

@@ -13,7 +13,7 @@ import { toast } from '@/lib/toast'
export function AdminCategoriesPage() { export function AdminCategoriesPage() {
const [categories, setCategories] = useState<StepCategoryListItem[]>([]) const [categories, setCategories] = useState<StepCategoryListItem[]>([])
const [allSteps, setAllSteps] = useState<any[]>([]) const [allSteps, setAllSteps] = useState<{ category_id?: string }[]>([])
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [showCreateModal, setShowCreateModal] = useState(false) const [showCreateModal, setShowCreateModal] = useState(false)
const [showEditModal, setShowEditModal] = useState(false) const [showEditModal, setShowEditModal] = useState(false)

View File

@@ -75,7 +75,7 @@ export function SessionHistoryPage() {
const loadSessions = async () => { const loadSessions = async () => {
setIsLoading(true) setIsLoading(true)
try { try {
const params: any = {} const params: Record<string, string | boolean> = {}
// Tab filter (all/active/completed) // Tab filter (all/active/completed)
if (filter !== 'all') { if (filter !== 'all') {