chore: remove obsolete v3/v4 version copy from the dashboard (#4589)

This commit is contained in:
claude[bot]
2026-08-13 13:51:42 +01:00
committed by GitHub
parent 802d23836d
commit f6f3b75547
17 changed files with 746 additions and 955 deletions
@@ -0,0 +1,6 @@
---
area: webapp
type: improvement
---
Out-of-date upgrade prompts no longer appear in the dashboard: the "V4" badges and the notices saying preview branches and the queues table need V4 have been removed. The side menu still warns you when a project is on v3, with updated wording and a link to the v4 upgrade guide.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

@@ -7,7 +7,6 @@ import {
PlusIcon,
QuestionMarkCircleIcon,
RectangleGroupIcon,
RectangleStackIcon,
SparklesIcon,
Squares2X2Icon,
} from "@heroicons/react/20/solid";
@@ -63,7 +62,6 @@ import {
TriggerDevStepV3,
} from "./SetupCommands";
import { StepContentContainer } from "./StepContentContainer";
import { V4Badge } from "./V4Badge";
/**
* What the agent is asked when it's opened from a deployment setup panel. The panel is the docs
@@ -475,36 +473,6 @@ export function AlertsNoneDeployed() {
);
}
export function QueuesHasNoTasks() {
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();
return (
<InfoPanel
title="You don't have any queues"
icon={RectangleStackIcon}
iconClassName="text-queues"
panelClassName="max-w-md"
accessory={
<LinkButton
to={v3EnvironmentPath(organization, project, environment)}
variant="primary/small"
>
Create a task
</LinkButton>
}
>
<Paragraph spacing variant="small">
Queues will appear here when you have created a task in this environment. Follow the
instructions on the{" "}
<TextLink to={v3EnvironmentPath(organization, project, environment)}>Tasks page</TextLink>{" "}
to create a task, then return here to see its queue.
</Paragraph>
</InfoPanel>
);
}
export function NoWaitpointTokens() {
return (
<InfoPanel
@@ -567,14 +535,10 @@ export function BranchesNoBranchableEnvironment({ showSelfServe }: { showSelfSer
)
}
>
<Paragraph spacing variant="small">
<Paragraph variant="small">
Preview branches in Trigger.dev create isolated environments for testing new features before
production.
</Paragraph>
<Paragraph variant="small">
You must be on <V4Badge inline /> to access preview branches. Read our{" "}
<TextLink to={docsPath("upgrade-to-v4")}>upgrade to v4 guide</TextLink> to learn more.
</Paragraph>
</InfoPanel>
);
}
@@ -648,14 +612,10 @@ export function BranchesNoBranches({
/>
}
>
<Paragraph spacing variant="small">
<Paragraph variant="small">
Branches are a way to test new features in isolation before merging them into the main
environment.
</Paragraph>
<Paragraph spacing variant="small">
Branches are only available when using <V4Badge inline /> or above. Read our{" "}
<TextLink to={docsPath("upgrade-to-v4")}>v4 upgrade guide</TextLink> to learn more.
</Paragraph>
</InfoPanel>
);
}
-26
View File
@@ -1,26 +0,0 @@
import { cn } from "~/utils/cn";
import { Badge } from "./primitives/Badge";
import { SimpleTooltip } from "./primitives/Tooltip";
export function V4Badge({ inline = false, className }: { inline?: boolean; className?: string }) {
return (
<SimpleTooltip
button={
<Badge variant="extra-small" className={cn(inline ? "inline-grid" : "", className)}>
V4
</Badge>
}
content="This feature is only available in V4 and above."
disableHoverableContent
/>
);
}
export function V4Title({ children }: { children: React.ReactNode }) {
return (
<>
<span>{children}</span>
<V4Badge />
</>
);
}
@@ -11,7 +11,7 @@ import { useFeatures } from "~/hooks/useFeatures";
import { useOrganization, type MatchedOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { cn } from "~/utils/cn";
import { branchesPath, branchesDevPath, docsPath, v3BillingPath } from "~/utils/pathBuilder";
import { branchesPath, branchesDevPath, v3BillingPath } from "~/utils/pathBuilder";
import {
EnvironmentCombo,
EnvironmentIcon,
@@ -29,9 +29,7 @@ import {
PopoverSectionHeader,
PopoverTrigger,
} from "../primitives/Popover";
import { TextLink } from "../primitives/TextLink";
import { SimpleTooltip } from "../primitives/Tooltip";
import { V4Badge } from "../V4Badge";
import { type SideMenuEnvironment, type SideMenuProject } from "./SideMenu";
import { Badge } from "../primitives/Badge";
@@ -411,14 +409,10 @@ export function BranchesPopoverContent({
<BranchEnvironmentIconSmall className={cn("size-4", envTextClassName)} />
<Header2>Create your first branch</Header2>
</div>
<Paragraph spacing variant="small">
<Paragraph variant="small">
Branches are a way to test new features in isolation before merging them into the main
environment.
</Paragraph>
<Paragraph variant="small">
Branches are only available when using <V4Badge inline /> or above. Read our{" "}
<TextLink to={docsPath("upgrade-to-v4")}>v4 upgrade guide</TextLink> to learn more.
</Paragraph>
</div>
) : (
<div className="flex max-w-sm flex-col gap-1 p-2">
@@ -96,6 +96,7 @@ import {
adminPath,
branchesPath,
concurrencyPath,
docsPath,
limitsPath,
logoutPath,
newOrganizationPath,
@@ -1111,7 +1112,7 @@ export function SideMenu({
isDragging={isDragging}
className="min-w-0 flex-1"
/>
{environment.type === "DEVELOPMENT" && project.engine === "V2" && (
{environment.type === "DEVELOPMENT" && (
<CollapsibleElement isDragging={isDragging} className="shrink-0">
<Dialog>
<TooltipProvider disableHoverableContent={true}>
@@ -1604,7 +1605,7 @@ function V3DeprecationPanel({
<ExclamationTriangleIcon className="size-5 text-amber-400" />
</PopoverTrigger>
}
content="V3 deprecation warning"
content="v3 is now deprecated"
side="right"
sideOffset={8}
disableHoverableContent
@@ -1625,23 +1626,23 @@ function V3DeprecationContent() {
<div className="flex items-center gap-1 border-b border-amber-500/30 pb-1">
<ExclamationTriangleIcon className="size-4 text-amber-400" />
<Paragraph variant="small/bright" className="text-amber-300">
V3 deprecation warning
v3 is now deprecated
</Paragraph>
</div>
<Paragraph variant="extra-small/bright" className="text-amber-300">
This is a v3 project. V3 deploys will stop working on 1 April 2026. Full shutdown is 1 July
2026 where all v3 runs will stop executing. Migrate to v4 to avoid downtime.
This is a v3 project which is now deprecated so no runs are executing. Upgrade to v4 to
resume executing runs in this project.
</Paragraph>
<LinkButton
variant="secondary/small"
to="https://trigger.dev/docs/migrating-from-v3"
to={docsPath("/upgrade-to-v4")}
target="_blank"
fullWidth
TrailingIcon={ArrowTopRightOnSquareIcon}
trailingIconClassName="text-amber-300"
className="border-amber-500/30 bg-amber-500/15 hover:border-amber-500/50! hover:bg-amber-500/25!"
>
<span className="text-amber-300">View migration guide</span>
<span className="text-amber-300">Upgrade to v4</span>
</LinkButton>
</div>
);
@@ -5,7 +5,6 @@ import { type PrismaClientOrTransaction } from "~/db.server";
import { type AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
import { logger } from "~/services/logger.server";
import { determineEngineVersion } from "~/v3/engineVersion.server";
import { engine } from "~/v3/runEngine.server";
import { BasePresenter } from "./basePresenter.server";
import { toQueueItem } from "./QueueRetrievePresenter.server";
@@ -51,25 +50,12 @@ type QueueListPagination =
| { mode: "filtered"; currentPage: number; hasMore: boolean }
| { mode: "unfiltered"; currentPage: number; totalPages: number; count: number };
// The `?: undefined` markers keep every key reachable across the union, so consumers
// can destructure before narrowing on `success`.
export type QueueListResult =
| {
success: false;
code: string;
totalQueues: number;
hasFilters: boolean;
queues?: undefined;
pagination?: undefined;
}
| {
success: true;
queues: QueueListItem[];
pagination: QueueListPagination;
totalQueues?: number;
hasFilters: boolean;
code?: undefined;
};
export type QueueListResult = {
queues: QueueListItem[];
pagination: QueueListPagination;
totalQueues?: number;
hasFilters: boolean;
};
function formatClickhouseDateTime(date: Date): string {
return date.toISOString().slice(0, 19).replace("T", " ");
@@ -126,37 +112,6 @@ export class QueueListPresenter extends BasePresenter {
}): Promise<QueueListResult> {
const hasFilters = Boolean(query?.trim()) || type !== undefined;
const engineVersion = await determineEngineVersion({ environment });
if (engineVersion === "V1") {
const totalQueues = await this._replica.taskQueue.count({
where: buildQueueListWhere(environment.id, query, type),
});
if (totalQueues === 0) {
const oldQueue = await this._replica.taskQueue.findFirst({
where: {
runtimeEnvironmentId: environment.id,
version: "V1",
},
});
if (oldQueue) {
return {
success: false as const,
code: "engine-version",
totalQueues: 1,
hasFilters,
};
}
}
return {
success: false as const,
code: "engine-version",
totalQueues,
hasFilters,
};
}
if (sort !== "name") {
// Ranking is additive: any failure or unsupported input falls back to name order.
try {
@@ -173,7 +128,6 @@ export class QueueListPresenter extends BasePresenter {
const { queues, hasMore } = await this.getFilteredQueues(environment, query, page, type);
return {
success: true as const,
queues,
pagination: {
mode: "filtered" as const,
@@ -189,7 +143,6 @@ export class QueueListPresenter extends BasePresenter {
});
return {
success: true as const,
queues: await this.getUnfilteredQueues(environment, page, type),
pagination: {
mode: "unfiltered" as const,
@@ -300,7 +253,6 @@ export class QueueListPresenter extends BasePresenter {
}
return {
success: true as const,
queues: await this.enrichQueues(environment, [...rankedPageQueues, ...tailQueues]),
pagination: {
mode: "unfiltered" as const,
@@ -7,7 +7,6 @@ import {
type TaskQueueType,
} from "@trigger.dev/database";
import { type AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { determineEngineVersion } from "~/v3/engineVersion.server";
import { engine } from "~/v3/runEngine.server";
import { BasePresenter } from "./basePresenter.server";
@@ -80,16 +79,6 @@ export class QueueRetrievePresenter extends BasePresenter {
environment: AuthenticatedEnvironment;
queueInput: RetrieveQueueParam;
}) {
//check the engine is the correct version
const engineVersion = await determineEngineVersion({ environment });
if (engineVersion === "V1") {
return {
success: false as const,
code: "engine-version",
};
}
const queue = await getQueue(this._replica, environment, queueInput);
if (!queue) {
return {
@@ -14,7 +14,6 @@ import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons";
import { BranchesNoBranchableEnvironment, BranchesNoBranches } from "~/components/BlankStatePanels";
import { Feedback } from "~/components/Feedback";
import { GitMetadata } from "~/components/GitMetadata";
import { V4Title } from "~/components/V4Badge";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout";
import { Badge } from "~/components/primitives/Badge";
@@ -231,7 +230,7 @@ export default function Page() {
return (
<PageContainer>
<NavBar>
<PageTitle title={<V4Title>Preview branches</V4Title>} />
<PageTitle title="Preview branches" />
</NavBar>
<PageBody>
<MainCenteredContainer className="max-w-md">
@@ -245,7 +244,7 @@ export default function Page() {
return (
<PageContainer>
<NavBar>
<PageTitle title={<V4Title>Preview branches</V4Title>} />
<PageTitle title="Preview branches" />
<PageAccessories>
<AdminDebugTooltip>
<Property.Table>
@@ -6,7 +6,6 @@ import { useCallback } from "react";
import { SearchInput } from "~/components/primitives/SearchInput";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons";
import { V4Title } from "~/components/V4Badge";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import { Badge } from "~/components/primitives/Badge";
@@ -98,7 +97,7 @@ export default function Page() {
return (
<PageContainer>
<NavBar>
<PageTitle title={<V4Title>Dev branches</V4Title>} />
<PageTitle title="Dev branches" />
<PageAccessories>
<AdminDebugTooltip>
<Property.Table>
@@ -166,16 +166,13 @@ export default function Page() {
const { data, rootOnlyDefault, filters, canCancelRuns, canReplayRuns } =
useTypedLoaderData<typeof loader>();
const { isConnected } = useDevPresence();
const project = useProject();
const environment = useEnvironment();
return (
<>
<NavBar>
<PageTitle title="Runs" accessory={<RunsHelpTooltip />} />
{environment.type === "DEVELOPMENT" && project.engine === "V2" && (
<DevDisconnectedBanner isConnected={isConnected} />
)}
{environment.type === "DEVELOPMENT" && <DevDisconnectedBanner isConnected={isConnected} />}
<PageAccessories>
<AdminDebugTooltip />
<WhenAgentUnavailable>
@@ -35,7 +35,6 @@ import {
WaitpointSearchParamsSchema,
WaitpointTokenFilters,
} from "~/components/runs/v3/WaitpointTokenFilters";
import { V4Title } from "~/components/V4Badge";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
@@ -138,7 +137,7 @@ export default function Page() {
return (
<PageContainer>
<NavBar>
<PageTitle title={<V4Title>Waitpoint Tokens</V4Title>} />
<PageTitle title="Waitpoint Tokens" />
<PageAccessories>
<AdminDebugTooltip />
<WhenAgentUnavailable>
@@ -3,6 +3,7 @@ import { type QueueItem, type RetrieveQueueParam, RetrieveQueueType } from "@tri
import { z } from "zod";
import { QueueRetrievePresenter } from "~/presenters/v3/QueueRetrievePresenter.server";
import { createLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { determineEngineVersion } from "~/v3/engineVersion.server";
const SearchParamsSchema = z.object({
type: RetrieveQueueType.default("id"),
@@ -24,6 +25,15 @@ export const loader = createLoaderApiRoute(
},
},
async ({ params, searchParams, authentication }) => {
// v3 (engine V1) has no V2 queues to retrieve, so old clients get a clean 400.
const engineVersion = await determineEngineVersion({
environment: authentication.environment,
});
if (engineVersion === "V1") {
return json({ error: "engine-version" }, { status: 400 });
}
const input: RetrieveQueueParam =
searchParams.type === "id"
? params.queueParam
@@ -39,11 +49,7 @@ export const loader = createLoaderApiRoute(
});
if (!result.success) {
if (result.code === "queue-not-found") {
return json({ error: result.code }, { status: 404 });
}
return json({ error: result.code }, { status: 400 });
return json({ error: result.code }, { status: 404 });
}
const q: QueueItem = result.queue;
+10 -4
View File
@@ -8,6 +8,7 @@ import {
import { toOffsetLimitQueueListPagination } from "~/presenters/v3/queueListPagination.server";
import { logger } from "~/services/logger.server";
import { createLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { determineEngineVersion } from "~/v3/engineVersion.server";
import { ServiceValidationError } from "~/v3/services/baseService.server";
const SearchParamsSchema = z.object({
@@ -30,15 +31,20 @@ export const loader = createLoaderApiRoute(
const service = new QueueListPresenter(searchParams.perPage);
try {
// v3 (engine V1) has no V2 queues to list, so old clients get a clean 400.
const engineVersion = await determineEngineVersion({
environment: authentication.environment,
});
if (engineVersion === "V1") {
return json({ error: "engine-version" }, { status: 400 });
}
const result = await service.call({
environment: authentication.environment,
page: searchParams.page ?? 1,
});
if (!result.success) {
return json({ error: result.code }, { status: 400 });
}
const queues: QueueItem[] = result.queues;
return json(
{
@@ -51,15 +51,6 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
type,
});
if (!result.success) {
return {
queues: [],
currentPage: 1,
hasMore: false,
hasFilters: Boolean(query?.trim()) || Boolean(type),
};
}
return {
queues: result.queues.map((queue) => ({
id: queue.id,
@@ -88,7 +88,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
type,
});
return {
queues: queues.success ? queues.queues.map((q) => q.name) : [],
queues: queues.queues.map((q) => q.name),
};
},
};