diff --git a/dist/index.html b/dist/index.html
index 9e15ea1..db73129 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -6,7 +6,7 @@
Todo App
-
+
diff --git a/src/components/AddTask.tsx b/src/components/AddTask.tsx
index 13d52b5..995ae50 100644
--- a/src/components/AddTask.tsx
+++ b/src/components/AddTask.tsx
@@ -1,5 +1,5 @@
import { useState, useRef, useEffect } from 'react';
-import { Plus, Calendar, Flag, Tag, X } from 'lucide-react';
+import { Plus, Calendar, X } from 'lucide-react';
import type { Priority } from '@/types';
import { cn, getPriorityColor } from '@/lib/utils';
import { useTasksStore } from '@/stores/tasks';
diff --git a/src/components/CompletedSection.tsx b/src/components/CompletedSection.tsx
index 37824e8..4b35047 100644
--- a/src/components/CompletedSection.tsx
+++ b/src/components/CompletedSection.tsx
@@ -1,7 +1,6 @@
import { useState } from 'react';
import { ChevronRight, ChevronDown, Check } from 'lucide-react';
import type { Task } from '@/types';
-import { cn } from '@/lib/utils';
import { useTasksStore } from '@/stores/tasks';
interface CompletedSectionProps {
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index 9dc3b0a..c6ef3eb 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -2,7 +2,7 @@ import { useState, useRef, useEffect } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import {
Inbox, Calendar, CalendarDays, Plus, ChevronDown, ChevronRight,
- Hash, Settings, LogOut, User, FolderPlus, Tag, X, Check,
+ Settings, LogOut, Tag, X,
PanelLeftClose, PanelLeftOpen
} from 'lucide-react';
import { cn } from '@/lib/utils';
@@ -56,7 +56,7 @@ export function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
setIsCollapsed(next);
try {
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(next));
- } catch {}
+ } catch { /* ignore localStorage errors */ }
};
const handleNavClick = () => {
@@ -94,7 +94,6 @@ export function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
navigate('/login');
};
- const inbox = projects.find(p => p.isInbox);
const regularProjects = projects.filter(p => !p.isInbox);
const navItems = [
diff --git a/src/components/TaskDetail.tsx b/src/components/TaskDetail.tsx
index a0b3784..755b4ec 100644
--- a/src/components/TaskDetail.tsx
+++ b/src/components/TaskDetail.tsx
@@ -4,7 +4,7 @@ import {
Tag, MessageSquare, ChevronRight, AlertCircle, Layers
} from 'lucide-react';
import type { Task, Priority, Comment, Section } from '@/types';
-import { cn, formatDate, getPriorityColor, getPriorityLabel } from '@/lib/utils';
+import { cn, getPriorityColor } from '@/lib/utils';
import { useTasksStore } from '@/stores/tasks';
import { api } from '@/lib/api';
@@ -29,7 +29,9 @@ export function TaskDetail({ task, onClose }: TaskDetailProps) {
const titleRef = useRef(null);
const panelRef = useRef(null);
+ // Sync local state from task prop - intentional setState in effect for controlled form
useEffect(() => {
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- syncing local form state from prop
setTitle(task.title);
setDescription(task.description || '');
setDueDate(task.dueDate ? task.dueDate.slice(0, 10) : '');
@@ -50,7 +52,7 @@ export function TaskDetail({ task, onClose }: TaskDetailProps) {
if (projectId) {
const proj = projects.find(p => p.id === projectId);
if (proj?.sections) {
- setAvailableSections(proj.sections);
+ setAvailableSections(proj.sections); // eslint-disable-line react-hooks/set-state-in-effect
} else {
api.getProject(projectId).then((p) => {
setAvailableSections(p.sections || []);
diff --git a/src/lib/__tests__/api.test.ts b/src/lib/__tests__/api.test.ts
index d29cd64..cb1e849 100644
--- a/src/lib/__tests__/api.test.ts
+++ b/src/lib/__tests__/api.test.ts
@@ -1,25 +1,23 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
+import type { Mock } from 'vitest';
// We need to test the ApiClient class, so we import the module fresh
// The api.ts uses import.meta.env.PROD which is false in test
// So API_BASE = '/api' and AUTH_BASE = ''
-let ApiClient: any;
-let api: any;
+let api: Record unknown>;
beforeEach(async () => {
vi.resetModules();
vi.stubGlobal('fetch', vi.fn());
const mod = await import('@/lib/api');
- api = mod.api;
- // Get the class from the singleton
- ApiClient = (api as any).constructor;
+ api = mod.api as unknown as Record unknown>;
});
-function mockFetchResponse(data: any, ok = true, status = 200) {
- (fetch as any).mockResolvedValueOnce({
+function mockFetchResponse(data: unknown, ok = true, _status = 200) {
+ (fetch as Mock).mockResolvedValueOnce({
ok,
- status,
+ status: _status,
json: () => Promise.resolve(data),
});
}
@@ -42,7 +40,7 @@ describe('ApiClient', () => {
});
it('throws on failed login', async () => {
- (fetch as any).mockResolvedValueOnce({
+ (fetch as Mock).mockResolvedValueOnce({
ok: false,
json: () => Promise.resolve({ message: 'Invalid credentials' }),
});
@@ -78,7 +76,7 @@ describe('ApiClient', () => {
});
it('returns null when not authenticated', async () => {
- (fetch as any).mockResolvedValueOnce({
+ (fetch as Mock).mockResolvedValueOnce({
ok: false,
json: () => Promise.resolve({}),
});
@@ -88,7 +86,7 @@ describe('ApiClient', () => {
});
it('returns null on network error', async () => {
- (fetch as any).mockRejectedValueOnce(new Error('Network error'));
+ (fetch as Mock).mockRejectedValueOnce(new Error('Network error'));
const result = await api.getSession();
expect(result).toBeNull();
@@ -165,7 +163,7 @@ describe('ApiClient', () => {
await api.getTasks({ projectId: 'proj1', completed: false, today: true });
- const calledUrl = (fetch as any).mock.calls[0][0] as string;
+ const calledUrl = (fetch as Mock).mock.calls[0][0] as string;
expect(calledUrl).toContain('/api/tasks?');
expect(calledUrl).toContain('projectId=proj1');
expect(calledUrl).toContain('completed=false');
@@ -259,14 +257,14 @@ describe('ApiClient', () => {
await api.getProjects();
- const headers = (fetch as any).mock.calls[0][1].headers;
+ const headers = (fetch as Mock).mock.calls[0][1].headers;
expect(headers['Authorization']).toBeUndefined();
});
});
describe('Error handling', () => {
it('throws error with message from non-200 response', async () => {
- (fetch as any).mockResolvedValueOnce({
+ (fetch as Mock).mockResolvedValueOnce({
ok: false,
json: () => Promise.resolve({ error: 'Not found' }),
});
@@ -275,7 +273,7 @@ describe('ApiClient', () => {
});
it('throws "Request failed" when error body is unparseable', async () => {
- (fetch as any).mockResolvedValueOnce({
+ (fetch as Mock).mockResolvedValueOnce({
ok: false,
json: () => Promise.reject(new Error('parse error')),
});
diff --git a/src/pages/Board.tsx b/src/pages/Board.tsx
index 7e71706..46d5c10 100644
--- a/src/pages/Board.tsx
+++ b/src/pages/Board.tsx
@@ -16,7 +16,7 @@ interface BoardViewProps {
}
function TaskCard({ task, muted }: { task: Task; muted?: boolean }) {
- const { toggleComplete, setSelectedTask } = useTasksStore();
+ const { setSelectedTask } = useTasksStore();
const overdue = !task.isCompleted && isOverdue(task.dueDate);
return (
@@ -84,6 +84,7 @@ function EditableHeader({
title,
count,
sectionId,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
projectId,
onRename,
onDelete,
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
index 9cdfb37..7513f65 100644
--- a/src/pages/Login.tsx
+++ b/src/pages/Login.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react';
-import { useNavigate, Link } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
import { useAuthStore } from '@/stores/auth';
export function LoginPage() {
diff --git a/src/pages/Project.tsx b/src/pages/Project.tsx
index 28efcef..862fb7e 100644
--- a/src/pages/Project.tsx
+++ b/src/pages/Project.tsx
@@ -1,6 +1,6 @@
import { useEffect, useState, useRef } from 'react';
import { useParams, useNavigate, useLocation } from 'react-router-dom';
-import { LayoutList, LayoutGrid, Plus, Pencil, Trash2, AlertCircle } from 'lucide-react';
+import { LayoutList, LayoutGrid, Pencil, Trash2, AlertCircle } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useTasksStore } from '@/stores/tasks';
import { TaskItem } from '@/components/TaskItem';
diff --git a/src/stores/auth.ts b/src/stores/auth.ts
index 1390a98..1fc030c 100644
--- a/src/stores/auth.ts
+++ b/src/stores/auth.ts
@@ -17,7 +17,7 @@ interface AuthState {
export const useAuthStore = create()(
persist(
- (set, get) => ({
+ (set) => ({
user: null,
isLoading: true,
isAuthenticated: false,