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>
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
import type { Task } from "@mosaic/shared";
|
|
import { TaskPriority } from "@mosaic/shared";
|
|
import { formatDate } from "@/lib/utils/date-format";
|
|
import { TaskStatus } from "@mosaic/shared";
|
|
import Link from "next/link";
|
|
|
|
interface RecentTasksWidgetProps {
|
|
tasks: Task[];
|
|
isLoading: boolean;
|
|
}
|
|
|
|
const statusIcons: Record<TaskStatus, string> = {
|
|
[TaskStatus.NOT_STARTED]: "⚪",
|
|
[TaskStatus.IN_PROGRESS]: "🟢",
|
|
[TaskStatus.PAUSED]: "⏸️",
|
|
[TaskStatus.COMPLETED]: "✅",
|
|
[TaskStatus.ARCHIVED]: "💤",
|
|
};
|
|
|
|
export function RecentTasksWidget({ tasks, isLoading }: RecentTasksWidgetProps): React.JSX.Element {
|
|
if (isLoading) {
|
|
return (
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
<div className="flex justify-center items-center">
|
|
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-gray-900"></div>
|
|
<span className="ml-3 text-gray-600">Loading tasks...</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const recentTasks = tasks.slice(0, 5);
|
|
|
|
return (
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-lg font-semibold text-gray-900">Recent Tasks</h2>
|
|
<Link href="/tasks" className="text-sm text-blue-600 hover:text-blue-700">
|
|
View all →
|
|
</Link>
|
|
</div>
|
|
{recentTasks.length === 0 ? (
|
|
<p className="text-sm text-gray-500 text-center py-4">No tasks yet</p>
|
|
) : (
|
|
<ul className="space-y-3">
|
|
{recentTasks.map((task) => (
|
|
<li
|
|
key={task.id}
|
|
className="flex items-start gap-3 p-3 rounded-lg hover:bg-gray-50 transition-colors"
|
|
>
|
|
<span className="text-lg flex-shrink-0" aria-label={`Status: ${task.status}`}>
|
|
{statusIcons[task.status]}
|
|
</span>
|
|
<div className="flex-1 min-w-0">
|
|
<h3 className="font-medium text-gray-900 text-sm truncate">{task.title}</h3>
|
|
<div className="flex items-center gap-2 mt-1">
|
|
{task.priority !== TaskPriority.LOW && (
|
|
<span
|
|
className={`text-xs px-2 py-0.5 rounded-full ${
|
|
task.priority === TaskPriority.HIGH
|
|
? "bg-red-100 text-red-700"
|
|
: "bg-blue-100 text-blue-700"
|
|
}`}
|
|
>
|
|
{task.priority}
|
|
</span>
|
|
)}
|
|
{task.dueDate && (
|
|
<span className="text-xs text-gray-500">{formatDate(task.dueDate)}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|