fix: Resolve all ESLint errors and warnings in web package
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Fixes all 542 ESLint problems in the web package to achieve 0 errors and 0 warnings.

Changes:
- Fixed 144 issues: nullish coalescing, return types, unused variables
- Fixed 118 issues: unnecessary conditions, type safety, template literals
- Fixed 79 issues: non-null assertions, unsafe assignments, empty functions
- Fixed 67 issues: explicit return types, promise handling, enum comparisons
- Fixed 45 final warnings: missing return types, optional chains
- Fixed 25 typecheck-related issues: async/await, type assertions, formatting
- Fixed JSX.Element namespace errors across 90+ files

All Quality Rails violations resolved. Lint and typecheck both pass with 0 problems.

Files modified: 118 components, tests, hooks, and utilities

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 00:10:03 -06:00
parent f0704db560
commit ac1f2c176f
117 changed files with 749 additions and 505 deletions

View File

@@ -9,7 +9,7 @@ describe("GanttChart", (): void => {
const baseDate = new Date("2026-02-01T00:00:00Z");
const createGanttTask = (overrides: Partial<GanttTask> = {}): GanttTask => ({
id: `task-${Math.random()}`,
id: `task-${String(Math.random())}`,
workspaceId: "workspace-1",
title: "Sample Task",
description: null,
@@ -90,7 +90,8 @@ describe("GanttChart", (): void => {
render(<GanttChart tasks={tasks} />);
const taskRow = screen.getAllByText("Completed Task")[0]!.closest("[role='row']");
const taskElements = screen.getAllByText("Completed Task");
const taskRow = taskElements[0]?.closest("[role='row']");
expect(taskRow?.className).toMatch(/Completed/i);
});
@@ -105,7 +106,8 @@ describe("GanttChart", (): void => {
render(<GanttChart tasks={tasks} />);
const taskRow = screen.getAllByText("Active Task")[0]!.closest("[role='row']");
const taskElements = screen.getAllByText("Active Task");
const taskRow = taskElements[0]?.closest("[role='row']");
expect(taskRow?.className).toMatch(/InProgress/i);
});
});
@@ -219,8 +221,8 @@ describe("GanttChart", (): void => {
expect(bars).toHaveLength(2);
// Second bar should be wider (more days)
const bar1Width = bars[0]!.style.width;
const bar2Width = bars[1]!.style.width;
const bar1Width = bars[0]?.style.width;
const bar2Width = bars[1]?.style.width;
// Basic check that widths are set (exact values depend on implementation)
expect(bar1Width).toBeTruthy();
@@ -344,11 +346,15 @@ describe("GanttChart", (): void => {
render(<GanttChart tasks={tasks} />);
const taskNames = screen.getAllByRole("row").map((row) => row.textContent);
const taskNames = screen.getAllByRole("row").map((row): string | null => row.textContent);
// Early Task should appear first
const earlyIndex = taskNames.findIndex((name) => name?.includes("Early Task"));
const lateIndex = taskNames.findIndex((name) => name?.includes("Late Task"));
const earlyIndex = taskNames.findIndex(
(name): boolean => name?.includes("Early Task") ?? false
);
const lateIndex = taskNames.findIndex(
(name): boolean => name?.includes("Late Task") ?? false
);
expect(earlyIndex).toBeLessThan(lateIndex);
});

View File

@@ -34,10 +34,22 @@ function calculateTimelineRange(tasks: GanttTask[]): TimelineRange {
};
}
let earliest = tasks[0]!.startDate;
let latest = tasks[0]!.endDate;
const firstTask = tasks[0];
if (!firstTask) {
// This should not happen due to the check above, but makes TS happy
const now = new Date();
const oneMonthLater = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
return {
start: now,
end: oneMonthLater,
totalDays: 30,
};
}
tasks.forEach((task) => {
let earliest = firstTask.startDate;
let latest = firstTask.endDate;
tasks.forEach((task): void => {
if (task.startDate < earliest) {
earliest = task.startDate;
}
@@ -82,8 +94,8 @@ function calculateBarPosition(
const widthPercent = (taskDuration / totalDays) * 100;
const result: GanttBarPosition = {
left: `${leftPercent}%`,
width: `${widthPercent}%`,
left: `${String(leftPercent)}%`,
width: `${String(widthPercent)}%`,
top: rowIndex * 48, // 48px row height
};
@@ -114,11 +126,11 @@ function getStatusClass(status: TaskStatus): string {
function getRowStatusClass(status: TaskStatus): string {
switch (status) {
case TaskStatus.COMPLETED:
return styles.rowCompleted || "";
return styles.rowCompleted ?? "";
case TaskStatus.IN_PROGRESS:
return styles.rowInProgress || "";
return styles.rowInProgress ?? "";
case TaskStatus.PAUSED:
return styles.rowPaused || "";
return styles.rowPaused ?? "";
default:
return "";
}
@@ -172,13 +184,16 @@ function calculateDependencyLines(
return;
}
task.dependencies.forEach((depId) => {
task.dependencies.forEach((depId): void => {
const fromIndex = taskIndexMap.get(depId);
if (fromIndex === undefined) {
return;
}
const fromTask = tasks[fromIndex]!;
const fromTask = tasks[fromIndex];
if (!fromTask) {
return;
}
// Calculate positions (as percentages)
const fromEndOffset = Math.max(

View File

@@ -201,8 +201,8 @@ describe("Gantt Types Helpers", (): void => {
const ganttTasks = toGanttTasks(tasks);
expect(ganttTasks).toHaveLength(2);
expect(ganttTasks[0]!.id).toBe("task-1");
expect(ganttTasks[1]!.id).toBe("task-2");
expect(ganttTasks[0]?.id).toBe("task-1");
expect(ganttTasks[1]?.id).toBe("task-2");
});
it("should filter out tasks that cannot be converted", (): void => {
@@ -240,9 +240,9 @@ describe("Gantt Types Helpers", (): void => {
const ganttTasks = toGanttTasks(tasks);
expect(ganttTasks[0]!.id).toBe("first");
expect(ganttTasks[1]!.id).toBe("second");
expect(ganttTasks[2]!.id).toBe("third");
expect(ganttTasks[0]?.id).toBe("first");
expect(ganttTasks[1]?.id).toBe("second");
expect(ganttTasks[2]?.id).toBe("third");
});
});
});

View File

@@ -78,25 +78,20 @@ function isStringArray(value: unknown): value is string[] {
*/
export function toGanttTask(task: Task): GanttTask | null {
// For Gantt chart, we need both start and end dates
const metadataStartDate = task.metadata?.startDate;
const metadataStartDate = task.metadata.startDate;
const startDate = isDateString(metadataStartDate) ? new Date(metadataStartDate) : task.createdAt;
const endDate = task.dueDate ?? new Date();
// Validate dates
if (!startDate || !endDate) {
return null;
}
// Extract dependencies with type guard
const metadataDependencies = task.metadata?.dependencies;
const metadataDependencies = task.metadata.dependencies;
const dependencies = isStringArray(metadataDependencies) ? metadataDependencies : undefined;
const ganttTask: GanttTask = {
...task,
startDate,
endDate,
isMilestone: task.metadata?.isMilestone === true,
isMilestone: task.metadata.isMilestone === true,
};
if (dependencies) {