fix(webapp): update task filter icons and the scheduled run icon (#4065)

## Summary

Fixes the Tasks icon on the Runs, Logs, Errors, and metrics pages (and
the task-derived queue indicators) to use the correct "T" tasks icon,
and shows a clock icon for scheduled runs in the run inspector header.

## Before
<img width="1440" height="704" alt="CleanShot 2026-06-28 at 21 39 57@2x"
src="https://github.com/user-attachments/assets/d00d5cbf-4726-4695-825b-59f5bdd84529"
/>

## After
<img width="1370" height="604" alt="CleanShot 2026-06-28 at 21 38 59@2x"
src="https://github.com/user-attachments/assets/fb709073-0b9f-409d-a852-72979bf14da6"
/>
This commit is contained in:
James Ritchie
2026-06-28 21:50:14 +01:00
committed by GitHub
parent b2b4c510e2
commit 9f29eb2090
9 changed files with 36 additions and 17 deletions
@@ -0,0 +1,6 @@
---
area: webapp
type: fix
---
Use the "all tasks" icon (`TasksIcon`) for the Tasks/Task type filter buttons on the Runs, Logs, Errors, and metrics dashboard pages (and the task-derived queue indicators), and show the clock icon for Scheduled runs in the run inspector header instead of the standard task icon.
@@ -14,7 +14,7 @@ import {
} from "~/components/primitives/Select";
import { useSearchParams } from "~/hooks/useSearchParam";
import { TaskTriggerSourceIcon } from "~/components/runs/v3/TaskTriggerSource";
import { TaskIcon } from "~/assets/icons/TaskIcon";
import { TasksIcon } from "~/assets/icons/TasksIcon";
import { appliedSummary, FilterMenuProvider } from "~/components/runs/v3/SharedFilters";
import { AppliedFilter } from "~/components/primitives/AppliedFilter";
@@ -41,7 +41,7 @@ export function LogsTaskFilter({ possibleTasks }: LogsTaskFilterProps) {
<TasksDropdown
trigger={
<SelectTrigger
icon={<TaskIcon className="size-4" />}
icon={<TasksIcon className="size-4" />}
variant="secondary/small"
shortcut={shortcut}
tooltipTitle="Filter by task"
@@ -67,7 +67,7 @@ export function LogsTaskFilter({ possibleTasks }: LogsTaskFilterProps) {
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
<AppliedFilter
label="Task"
icon={<TaskIcon className="size-4" />}
icon={<TasksIcon className="size-4" />}
value={appliedSummary(
selectedTasks.map((v) => {
const task = possibleTasks.find((task) => task.slug === v);
@@ -3,7 +3,7 @@ import { RectangleStackIcon } from "@heroicons/react/20/solid";
import { useFetcher } from "@remix-run/react";
import { matchSorter } from "match-sorter";
import { type ReactNode, useMemo } from "react";
import { TaskIcon } from "~/assets/icons/TaskIcon";
import { TasksIcon } from "~/assets/icons/TasksIcon";
import { AppliedFilter } from "~/components/primitives/AppliedFilter";
import {
ComboBox,
@@ -194,7 +194,7 @@ function QueuesDropdown({
className="text-text-bright"
icon={
queue.type === "task" ? (
<TaskIcon className="size-4 shrink-0 text-blue-500" />
<TasksIcon className="size-4 shrink-0 text-blue-500" />
) : (
<RectangleStackIcon className="size-4 shrink-0 text-purple-500" />
)
@@ -1,4 +1,4 @@
import { TaskIconSmall } from "~/assets/icons/TaskIcon";
import { TasksIcon } from "~/assets/icons/TasksIcon";
import { SimpleTooltip } from "~/components/primitives/Tooltip";
import { cn } from "~/utils/cn";
import { RectangleStackIcon } from "@heroicons/react/20/solid";
@@ -19,9 +19,7 @@ export function QueueName({
{type === "task" ? (
<SimpleTooltip
button={
<TaskIconSmall
className={cn("size-[1.125rem] text-blue-500", paused && "opacity-50")}
/>
<TasksIcon className={cn("size-[1.125rem] text-blue-500", paused && "opacity-50")} />
}
content={`This queue was automatically created from your "${name}" task`}
/>
@@ -22,7 +22,7 @@ import { ClockIcon } from "~/assets/icons/ClockIcon";
import { ListCheckedIcon } from "~/assets/icons/ListCheckedIcon";
import { MachineDefaultIcon } from "~/assets/icons/MachineIcon";
import { StatusIcon } from "~/assets/icons/StatusIcon";
import { TaskIcon } from "~/assets/icons/TaskIcon";
import { TasksIcon } from "~/assets/icons/TasksIcon";
import {
formatMachinePresetName,
MachineLabelCombo,
@@ -259,7 +259,7 @@ export function filterIcon(filterKey: string): ReactNode | undefined {
case "statuses":
return <StatusIcon className="size-4 border-text-bright" />;
case "tasks":
return <TaskIcon className="size-4" />;
return <TasksIcon className="size-4" />;
case "tags":
return <TagIcon className="size-4" />;
case "bulkId":
@@ -429,7 +429,7 @@ const filterTypes = [
{ name: "schedule", title: "Schedule ID", icon: <ClockIcon className="size-4" /> },
{ name: "bulk", title: "Bulk action", icon: <ListCheckedIcon className="size-4" /> },
{ name: "error", title: "Error ID", icon: <BugIcon className="size-4" /> },
{ name: "source", title: "Task type", icon: <TaskIcon className="size-4" /> },
{ name: "source", title: "Task type", icon: <TasksIcon className="size-4" /> },
] as const;
type FilterType = (typeof filterTypes)[number]["name"];
@@ -1239,7 +1239,7 @@ function QueuesDropdown({
value={queue.value}
icon={
queue.type === "task" ? (
<TaskIcon className="size-4 shrink-0 text-blue-500" />
<TasksIcon className="size-4 shrink-0 text-blue-500" />
) : (
<RectangleStackIcon className="size-4 shrink-0 text-purple-500" />
)
@@ -10,7 +10,7 @@ import { BookOpenIcon, CheckIcon } from "@heroicons/react/24/solid";
import { useLocation } from "@remix-run/react";
import { formatDuration, formatDurationMilliseconds } from "@trigger.dev/core/v3";
import { useCallback, useRef } from "react";
import { TaskIconSmall } from "~/assets/icons/TaskIcon";
import { TasksIcon } from "~/assets/icons/TasksIcon";
import { MachineLabelCombo } from "~/components/MachineLabelCombo";
import { MachineTooltipInfo } from "~/components/MachineTooltipInfo";
import { Badge } from "~/components/primitives/Badge";
@@ -470,7 +470,7 @@ export function TaskRunsTable({
buttonClassName="w-fit"
button={
<span className="flex items-center gap-1">
<TaskIconSmall className="size-[1.125rem] text-blue-500" />
<TasksIcon className="size-[1.125rem] text-blue-500" />
<span>{run.queue.name}</span>
</span>
}
@@ -297,6 +297,7 @@ export class SpanPresenter extends BasePresenter {
const taskKind = RunAnnotations.safeParse(run.annotations).data?.taskKind;
const isAgentRun = taskKind === "AGENT";
const isScheduled = taskKind === "SCHEDULED";
let region: { name: string; location: string | null } | null = null;
@@ -404,6 +405,7 @@ export class SpanPresenter extends BasePresenter {
isRunning: RUNNING_STATUSES.includes(run.status),
isError: isFailedRunStatus(run.status),
isAgentRun,
isScheduled,
payload,
payloadType: run.payloadType,
output,
@@ -392,12 +392,23 @@ function RunBody({
<div className="flex items-center justify-between gap-2 overflow-x-hidden px-3 pr-2">
<div className="flex items-center gap-1 overflow-x-hidden">
<RunIcon
name={run.isAgentRun ? "agent" : run.isCached ? "task-cached" : "task"}
name={
run.isAgentRun
? "agent"
: run.isScheduled
? "scheduled"
: run.isCached
? "task-cached"
: "task"
}
spanName={run.taskIdentifier}
className="size-5 min-h-5 min-w-5"
/>
<Header2
className={cn("overflow-x-hidden", run.isAgentRun ? "text-agents" : "text-blue-500")}
className={cn(
"overflow-x-hidden",
run.isAgentRun ? "text-agents" : run.isScheduled ? "text-schedules" : "text-blue-500"
)}
>
<span className="truncate">
{run.taskIdentifier}
@@ -72,6 +72,7 @@ export async function buildSyntheticSpanRun(args: {
const taskKind = RunAnnotations.safeParse(run.annotations).data?.taskKind;
const isAgentRun = taskKind === "AGENT";
const isScheduled = taskKind === "SCHEDULED";
const queueName = run.queue ?? "task/";
const isCancelled = run.status === "CANCELED";
@@ -149,6 +150,7 @@ export async function buildSyntheticSpanRun(args: {
isRunning: false,
isError: isFailed,
isAgentRun,
isScheduled,
payload,
payloadType: run.payloadType ?? "application/json",
output: undefined,