fix: resolve all 8 pre-existing lint errors (closes #29)
Fixed @typescript-eslint/no-explicit-any (4 occurrences): - FolderEditModal.tsx: proper error type checking instead of any - StepForm.tsx: explicit union type for visibility select - StepLibraryBrowser.tsx: explicit union types for stepType and sortBy selects Fixed react-hooks/set-state-in-effect (1 occurrence): - NodeEditorModal.tsx: replaced useEffect with direct state comparison Fixed @typescript-eslint/no-unused-vars (3 occurrences): - NodeEditorModal.tsx: removed unused useEffect import - NodeEditorModal.tsx: added eslint-disable for intentionally destructured children - usePermissions.ts: removed unused _tree parameter from canDeleteTree - TreeLibraryPage.tsx: updated canDeleteTree call site Fixed @typescript-eslint/no-empty-object-type (1 occurrence): - types/step.ts: changed empty interface to type alias Verification: - npm run lint: 0 errors (9 warnings are intentional exhaustive-deps) - npm run build: succeeds - TypeScript compilation: passes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -159,8 +159,11 @@ export function FolderEditModal({
|
||||
onClose()
|
||||
// Dispatch event to refresh folder list
|
||||
window.dispatchEvent(new Event('folder-changed'))
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.detail || 'Failed to save folder')
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error && 'response' in err
|
||||
? (err as { response?: { data?: { detail?: string } } }).response?.data?.detail
|
||||
: undefined
|
||||
setError(errorMessage || 'Failed to save folder')
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
<select
|
||||
id="visibility"
|
||||
value={visibility}
|
||||
onChange={(e) => setVisibility(e.target.value as any)}
|
||||
onChange={(e) => setVisibility(e.target.value as 'private' | 'team' | 'public')}
|
||||
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
>
|
||||
<option value="private">Private (only me)</option>
|
||||
|
||||
@@ -164,7 +164,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
||||
<select
|
||||
aria-label="Filter by step type"
|
||||
value={selectedStepType || ''}
|
||||
onChange={(e) => setSelectedStepType((e.target.value as any) || undefined)}
|
||||
onChange={(e) => setSelectedStepType((e.target.value as 'decision' | 'action' | 'solution') || undefined)}
|
||||
className="rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
>
|
||||
<option value="">All Types</option>
|
||||
@@ -190,7 +190,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
||||
<select
|
||||
aria-label="Sort steps by"
|
||||
value={sortBy}
|
||||
onChange={(e) => setSortBy(e.target.value as any)}
|
||||
onChange={(e) => setSortBy(e.target.value as 'recent' | 'popular' | 'highest_rated' | 'most_used')}
|
||||
className="rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
>
|
||||
<option value="recent">Most Recent</option>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useState, useCallback } from 'react'
|
||||
import { Modal } from '@/components/common/Modal'
|
||||
import { useTreeEditorStore } from '@/store/treeEditorStore'
|
||||
import { NodeFormDecision } from './NodeFormDecision'
|
||||
@@ -18,12 +18,14 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
|
||||
const nodeErrors = validationErrors.filter(e => e.nodeId === node.id)
|
||||
|
||||
// Local draft state - changes are NOT persisted until "Done" is clicked
|
||||
// Reset draft when node ID changes (switching to a different node)
|
||||
const [draft, setDraft] = useState<TreeStructure>(() => structuredClone(node))
|
||||
const [lastNodeId, setLastNodeId] = useState(node.id)
|
||||
|
||||
// Reset draft when node changes (e.g., external update)
|
||||
useEffect(() => {
|
||||
if (node.id !== lastNodeId) {
|
||||
setDraft(structuredClone(node))
|
||||
}, [node.id]) // Only reset when switching to a different node
|
||||
setLastNodeId(node.id)
|
||||
}
|
||||
|
||||
const handleUpdate = useCallback((updates: Partial<TreeStructure>) => {
|
||||
setDraft(prev => ({ ...prev, ...updates }))
|
||||
@@ -33,6 +35,7 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
|
||||
// Commit all draft changes to the store
|
||||
// IMPORTANT: Exclude 'children' from the update - children are managed separately
|
||||
// by addNode/deleteNode and we don't want to overwrite them with stale draft data
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { children, ...draftWithoutChildren } = draft
|
||||
updateNode(node.id, draftWithoutChildren)
|
||||
onClose()
|
||||
|
||||
@@ -55,7 +55,7 @@ export function usePermissions() {
|
||||
return false
|
||||
},
|
||||
|
||||
canDeleteTree: (_tree: { author_id: string | null }) => {
|
||||
canDeleteTree: () => {
|
||||
if (!user) return false
|
||||
return user.is_super_admin
|
||||
},
|
||||
|
||||
@@ -364,7 +364,7 @@ export function TreeLibraryPage() {
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Link>
|
||||
)}
|
||||
{canDeleteTree({ author_id: tree.author_id }) && (
|
||||
{canDeleteTree() && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
|
||||
@@ -119,6 +119,5 @@ export interface Rating {
|
||||
user_name?: string
|
||||
}
|
||||
|
||||
export interface Review extends Rating {
|
||||
// Same as Rating, just an alias for clarity
|
||||
}
|
||||
// Review is an alias for Rating
|
||||
export type Review = Rating
|
||||
|
||||
Reference in New Issue
Block a user