Fix all renderer lint errors

Remove dead imports and unused values across pages; replace the
MentionTextarea index-reset effect with an event-time reset; compute
budget chart segment offsets in a pure helper instead of mutating a
render-scope variable; drop the manual memoization the compiler could
not preserve in BillingPanel.
This commit is contained in:
ZacharyZhang-NY committed 2026-06-09 20:11:47 -04:00
1 parent a65347b91f
commit f964d052e2
10 files changed
+37 -35

No files matched your search

@@ -2,7 +2,6 @@ import { useMemo, useState, type FormEvent } from "react";
import { Avatar, Button, Field, Icon, IconButton, Pill } from "../shared/ui";
import { TopbarActions } from "../shared/TopbarActions";
import { Dialog } from "../shared/Dialog";
import type { DocumentRecord } from "../../../shared/domain";
import type { PageProps } from "./types";
import { activeOrgId, formatRelative } from "./types";
@@ -1,10 +1,10 @@
import { useMemo, useState } from "react";
import { Avatar, Button, Icon, Pill, Segmented } from "../shared/ui";
import { Avatar, Pill, Segmented } from "../shared/ui";
import { TopbarActions } from "../shared/TopbarActions";
import type { AgentRecord, ApprovalRecord, ChatMessageRecord, TaskRecord, TaskRunRecord } from "../../../shared/domain";
import { TASK_STATUS } from "../../../shared/domain";
import type { PageProps } from "./types";
import { activeOrgId, formatRelative, requireActiveEmployee } from "./types";
import { activeOrgId, formatRelative } from "./types";
type InboxFilter = "all" | "approvals" | "failures" | "blocked" | "messages";
const FILTERS: Array<{ id: InboxFilter; label: string }> = [
@@ -32,9 +32,8 @@ interface InboxItem {
tone: "amber" | "red" | "violet" | undefined;
}
export function InboxPage({ snapshot, actions, navigate }: PageProps) {
export function InboxPage({ snapshot }: PageProps) {
const orgId = activeOrgId(snapshot);
const employee = requireActiveEmployee(snapshot);
const agents = useMemo(() => snapshot.agents.filter((a) => a.orgId === orgId), [snapshot.agents, orgId]);
const tasks = useMemo(() => snapshot.tasks.filter((t) => t.orgId === orgId), [snapshot.tasks, orgId]);
const taskRuns = useMemo(() => snapshot.taskRuns.filter((r) => r.orgId === orgId), [snapshot.taskRuns, orgId]);
@@ -116,7 +115,6 @@ function PerformanceView({ agents, tasks, taskRuns }: { agents: AgentRecord[]; t
const completedRuns = taskRuns.filter((r) => r.state === "completed").length;
const failedRuns = taskRuns.filter((r) => r.state === "failed").length;
const successRate = completedRuns + failedRuns > 0 ? Math.round((completedRuns / (completedRuns + failedRuns)) * 100) : 0;
const throughput = doneTasks;
return (
<div>
@@ -3,7 +3,7 @@ import { Button, Field, Icon, IconButton, Pill } from "../shared/ui";
import { TopbarActions } from "../shared/TopbarActions";
import { Dialog } from "../shared/Dialog";
import type { PageProps } from "./types";
import { activeOrgId, formatRelative } from "./types";
import { activeOrgId } from "./types";
const MEETING_TYPES = ["standup", "all_hands", "sprint_review", "retrospective", "planning", "one_on_one", "ad_hoc"] as const;
@@ -1,6 +1,5 @@
import { useMemo, useState } from "react";
import { Avatar, Button, Icon, IconButton, Pill, Segmented } from "../shared/ui";
import { TopbarActions } from "../shared/TopbarActions";
import { Avatar, Pill, Segmented } from "../shared/ui";
import type { AgentRecord, ProviderRecord, TaskRecord, TaskRunRecord } from "../../../shared/domain";
import type { PageProps } from "./types";
import { activeOrgId, formatRelative } from "./types";
@@ -14,7 +13,7 @@ const FILTERS: Array<{ id: StatusFilter; label: string }> = [
{ id: "failed", label: "Failed" },
];
export function RunsPage({ snapshot, actions }: PageProps) {
export function RunsPage({ snapshot }: PageProps) {
const orgId = activeOrgId(snapshot);
const taskRuns = useMemo(() => snapshot.taskRuns.filter((r) => r.orgId === orgId), [snapshot.taskRuns, orgId]);
const tasks = useMemo(() => snapshot.tasks.filter((t) => t.orgId === orgId), [snapshot.tasks, orgId]);
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, type MutableRefObject } from "react";
import { useRef, useState, type MutableRefObject } from "react";
import { Avatar } from "../../shared/ui";
import type { AgentRecord, EmployeeRecord } from "../../../../shared/domain";
@@ -44,8 +44,6 @@ export function MentionTextarea({
? candidates.filter((c) => c.handle.toLowerCase().includes(query.toLowerCase()))
: [];
useEffect(() => { setIndex(0); }, [query]);
const close = () => setQuery(null);
const insertMention = (handle: string) => {
@@ -69,6 +67,7 @@ export function MentionTextarea({
if (atMatch) {
anchor.current = pos - atMatch[2].length - 1;
setQuery(atMatch[2]);
setIndex(0);
} else {
close();
}
@@ -39,30 +39,38 @@ export function BudgetUtilizationChart({ agents, providers }: { agents: AgentRec
});
const total = Math.max(1, data.reduce((s, d) => s + d.cents, 0));
const segments = stackSegments(data, total);
const colors = ["var(--accent)", "var(--green)", "var(--amber)", "var(--violet)", "var(--red)", "var(--text-3)"];
return (
<ChartCard title="Budget utilization">
<svg viewBox={`0 0 ${CHART_W} ${CHART_H}`} style={{ width: "100%", height: CHART_H }}>
{(() => {
let cumX = 0;
return data.map((d, i) => {
const w = (d.cents / total) * CHART_W;
const x = cumX;
cumX += w;
const colors = ["var(--accent)", "var(--green)", "var(--amber)", "var(--violet)", "var(--red)", "var(--text-3)"];
return (
<g key={d.label}>
<rect x={x} y={20} width={Math.max(1, w - 2)} height={CHART_H - 40} rx={4} fill={colors[i % colors.length]} opacity={0.8} />
<text x={x + w / 2} y={CHART_H - 4} textAnchor="middle" fontSize="9" fill="var(--text-3)">{d.label}</text>
<text x={x + w / 2} y={14} textAnchor="middle" fontSize="9" fill="var(--text-2)">${Math.round(d.cents / 100)}</text>
</g>
);
});
})()}
{segments.map((segment, i) => (
<g key={segment.label}>
<rect x={segment.x} y={20} width={Math.max(1, segment.w - 2)} height={CHART_H - 40} rx={4} fill={colors[i % colors.length]} opacity={0.8} />
<text x={segment.x + segment.w / 2} y={CHART_H - 4} textAnchor="middle" fontSize="9" fill="var(--text-3)">{segment.label}</text>
<text x={segment.x + segment.w / 2} y={14} textAnchor="middle" fontSize="9" fill="var(--text-2)">${Math.round(segment.cents / 100)}</text>
</g>
))}
</svg>
</ChartCard>
);
}
function stackSegments(
data: Array<{ label: string; cents: number }>,
total: number,
): Array<{ label: string; cents: number; x: number; w: number }> {
const segments: Array<{ label: string; cents: number; x: number; w: number }> = [];
let cumX = 0;
for (const d of data) {
const w = (d.cents / total) * CHART_W;
segments.push({ label: d.label, cents: d.cents, x: cumX, w });
cumX += w;
}
return segments;
}
export function RunActivityChart({ taskRuns }: { taskRuns: TaskRunRecord[] }) {
const buckets = last7DayBuckets(taskRuns.map((r) => r.createdAt));
const max = Math.max(1, ...buckets.map((b) => b.count));
@@ -1,7 +1,6 @@
import { useMemo } from "react";
import { Avatar, Pill } from "../../shared/ui";
import type { AgentRecord, HiringPolicyRecord, ProviderRecord, ProviderRequestRecord } from "../../../../shared/domain";
import { money, formatRelative } from "../types";
import { money } from "../types";
export function BillingPanel({
agents,
@@ -20,7 +19,7 @@ export function BillingPanel({
const totalInputChars = monthRequests.reduce((s, r) => s + r.inputCharCount, 0);
const totalOutputChars = monthRequests.reduce((s, r) => s + r.outputCharCount, 0);
const failedRequests = monthRequests.filter((r) => r.state === "failed");
const agentSpend = useMemo(() => buildAgentSpend(agents, providers, monthRequests), [agents, providers, monthRequests]);
const agentSpend = buildAgentSpend(agents, providers, monthRequests);
return (
<div className="page">
@@ -1,4 +1,4 @@
import { Avatar, Pill } from "../../shared/ui";
import { Avatar } from "../../shared/ui";
import type { AgentRecord, ApprovalRecord } from "../../../../shared/domain";
import { formatRelative } from "../types";
@@ -1,7 +1,7 @@
import { type FormEvent } from "react";
import { Field } from "../../shared/ui";
import { Dialog } from "../../shared/Dialog";
import type { AgentRecord, AppSnapshot } from "../../../../shared/domain";
import type { AgentRecord } from "../../../../shared/domain";
import type { SnapshotActions } from "../../app/useSnapshot";
function text(form: FormData, key: string): string {
@@ -11,7 +11,7 @@ import type {
TaskRunRecord,
WorkspaceRecord,
} from "../../../../shared/domain";
import { TASK_PRIORITY, TASK_STATUS } from "../../../../shared/domain";
import { TASK_STATUS } from "../../../../shared/domain";
import { auditEventLabel, formatRelative } from "../types";
import { canRun, nextStatus, prettyRunState, prettyStatus, providerFor, runsForTask, runTone } from "./task-helpers";
import type { SnapshotActions } from "../../app/useSnapshot";