chore: migrate lint to eslint flat config and finalize graph card status handling

This commit is contained in:
zenchantlive 2026-02-13 12:27:09 -08:00
parent a0261f181a
commit 2cfaa9b406
4 changed files with 30 additions and 4 deletions

19
eslint.config.mjs Normal file
View file

@ -0,0 +1,19 @@
import nextCoreWebVitals from 'eslint-config-next/core-web-vitals';
import nextTypeScript from 'eslint-config-next/typescript';
export default [
...nextCoreWebVitals,
...nextTypeScript,
{
ignores: ['nul'],
},
{
files: ['**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}'],
rules: {
'react-hooks/set-state-in-effect': 'off',
'prefer-const': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
},
];

View file

@ -7,7 +7,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"test": "node --test tests/bootstrap.test.mjs && node --import tsx --test tests/lib/parser.test.ts && node --import tsx --test tests/lib/pathing.test.ts && node --import tsx --test tests/lib/project-context.test.ts && node --import tsx --test tests/lib/project-scope.test.ts && node --import tsx --test tests/lib/aggregate-read.test.ts && node --import tsx --test tests/lib/kanban.test.ts && node --import tsx --test tests/lib/graph.test.ts && node --import tsx --test tests/lib/graph-view.test.ts && node --import tsx --test tests/lib/read-text-retry.test.ts && node --import tsx --test tests/lib/read-issues.test.ts && node --import tsx --test tests/lib/bd-path.test.ts && node --import tsx --test tests/lib/bridge.test.ts && node --import tsx --test tests/lib/mutations.test.ts && node --import tsx --test tests/lib/issue-editor.test.ts && node --import tsx --test tests/lib/writeback.test.ts && node --import tsx --test tests/lib/realtime.test.ts && node --import tsx --test tests/lib/coalescer.test.ts && node --import tsx --test tests/lib/watcher.test.ts && node --import tsx --test tests/lib/registry.test.ts && node --import tsx --test tests/lib/scanner.test.ts && node --import tsx --test tests/api/projects-route.test.ts && node --import tsx --test tests/api/mutations-routes.test.ts && node --import tsx --test tests/api/events-route.test.ts && node --test tests/guards/no-direct-jsonl-write.test.mjs && node --test tests/guards/no-inline-style-in-kanban.test.mjs && node --test tests/guards/kanban-responsive-contract.test.mjs && node --test tests/guards/graph-responsive-contract.test.mjs"
},

View file

@ -156,13 +156,14 @@ export function DependencyGraphPage({
() =>
issues
.filter((issue) => issue.issue_type === 'epic')
.filter((issue) => (!hideClosed ? true : issue.status !== 'closed'))
.sort((a, b) => {
// Push closed epics to the end
if (a.status === 'closed' && b.status !== 'closed') return 1;
if (b.status === 'closed' && a.status !== 'closed') return -1;
return a.id.localeCompare(b.id);
}),
[issues],
[issues, hideClosed],
);
// --- Derived data: tasks grouped by parent epic ---

View file

@ -161,6 +161,12 @@ function TaskCard({ issue, selected, blockedBy, blocks, blockers, blocking, isAc
const hasBlockers = blockers.length > 0; // Note: blockers list only contains OPEN blockers (computed in page)
const badge = statusBadge(issue.status, isActionable, hasBlockers);
const projectName = (issue as BeadIssue & { project?: { name?: string } }).project?.name ?? null;
// Determine effective status: in_progress always shows as in_progress, blocked always blocked, otherwise check blockers
const effectiveStatus: BeadIssue['status'] = issue.status === 'in_progress' ? 'in_progress' :
issue.status === 'blocked' ? 'blocked' :
hasBlockers ? 'blocked' :
issue.status;
return (
<div
@ -173,7 +179,7 @@ function TaskCard({ issue, selected, blockedBy, blocks, blockers, blocking, isAc
onSelect(issue.id, false);
}
}}
className={`group relative flex w-full flex-col rounded-xl border ${statusBorder(hasBlockers ? 'blocked' : issue.status)} ${statusGradient(hasBlockers ? 'blocked' : issue.status)} px-4 py-4 text-left transition duration-200 shadow-[0_4px_24px_rgba(0,0,0,0.35),inset_0_1px_0_rgba(255,255,255,0.06)] ${selected
className={`group relative flex w-full flex-col rounded-xl border ${statusBorder(effectiveStatus)} ${statusGradient(effectiveStatus)} px-4 py-4 text-left transition duration-200 shadow-[0_4px_24px_rgba(0,0,0,0.35),inset_0_1px_0_rgba(255,255,255,0.06)] ${selected
? 'ring-1 ring-amber-200/30 shadow-[0_0_20px_rgba(251,191,36,0.15)]'
: 'hover:shadow-[0_8px_30px_rgba(0,0,0,0.4)]'
}`}
@ -194,7 +200,7 @@ function TaskCard({ issue, selected, blockedBy, blocks, blockers, blocking, isAc
<div className="flex w-full items-start justify-between gap-3 pr-6">
<div className="flex flex-col gap-1.5 min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className={`h-2 w-2 rounded-full ${statusDot(hasBlockers ? 'blocked' : issue.status)} ring-1 ring-white/10`} />
<span className={`h-2 w-2 rounded-full ${statusDot(effectiveStatus)} ring-1 ring-white/10`} />
<span className="font-mono text-[10px] text-text-muted">{issue.id}</span>
{/* Status Badge */}
<span className={`rounded px-1.5 py-0.5 text-[9px] font-bold uppercase tracking-wider ${badge.textColor} ${badge.bgColor}`}>