Skip to content

Commit 978fe35

Browse files
committed
Show a toast when successfully ignored
1 parent 3e328e4 commit 978fe35

File tree

2 files changed

+49
-12
lines changed
  • apps/webapp/app

2 files changed

+49
-12
lines changed

apps/webapp/app/components/errors/ErrorStatusMenu.tsx

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import {
44
IconArrowBackUp as IconArrowBackUpBase,
55
IconBugOff as IconBugOffBase,
66
} from "@tabler/icons-react";
7-
import { useState } from "react";
7+
import { useEffect, useRef, useState } from "react";
88
import { type ErrorGroupStatus } from "@trigger.dev/database";
9-
import { Form, useNavigation, useSubmit } from "@remix-run/react";
9+
import { useFetcher } from "@remix-run/react";
1010
import { Button } from "~/components/primitives/Buttons";
11+
import { useToast } from "~/components/primitives/Toast";
1112
import { FormError } from "~/components/primitives/FormError";
1213
import { Input } from "~/components/primitives/Input";
1314
import { InputGroup } from "~/components/primitives/InputGroup";
@@ -31,6 +32,18 @@ const BugOffIcon = ({ className }: { className?: string }) => (
3132
<IconBugOffBase className={className} size={18} />
3233
);
3334

35+
export function ignoreActionToastMessage(data: Record<string, string>): string | undefined {
36+
if (data.action !== "ignore") return undefined;
37+
38+
const duration = data.duration ? Number(data.duration) : undefined;
39+
if (!duration) return "Error ignored indefinitely";
40+
41+
const hours = duration / (60 * 60 * 1000);
42+
if (hours < 24) return `Error ignored for ${hours} ${hours === 1 ? "hour" : "hours"}`;
43+
const days = hours / 24;
44+
return `Error ignored for ${days} ${days === 1 ? "day" : "days"}`;
45+
}
46+
3447
export function ErrorStatusMenuItems({
3548
status,
3649
taskIdentifier,
@@ -131,10 +144,19 @@ export function CustomIgnoreDialog({
131144
taskIdentifier: string;
132145
formAction?: string;
133146
}) {
134-
const submit = useSubmit();
135-
const navigation = useNavigation();
136-
const isSubmitting = navigation.state !== "idle";
147+
const fetcher = useFetcher<{ ok?: boolean }>();
148+
const isSubmitting = fetcher.state !== "idle";
137149
const [conditionError, setConditionError] = useState<string | null>(null);
150+
const toast = useToast();
151+
const hasHandledSuccess = useRef(false);
152+
153+
useEffect(() => {
154+
if (fetcher.state === "idle" && fetcher.data?.ok && !hasHandledSuccess.current) {
155+
hasHandledSuccess.current = true;
156+
toast.success("Error ignored with custom condition");
157+
onOpenChange(false);
158+
}
159+
}, [fetcher.state, fetcher.data, onOpenChange, toast]);
138160

139161
return (
140162
<Dialog open={open} onOpenChange={onOpenChange}>
@@ -145,7 +167,7 @@ export function CustomIgnoreDialog({
145167
Custom ignore condition
146168
</DialogTitle>
147169
</DialogHeader>
148-
<Form
170+
<fetcher.Form
149171
method="post"
150172
action={formAction}
151173
onSubmit={(e) => {
@@ -160,8 +182,8 @@ export function CustomIgnoreDialog({
160182
}
161183

162184
setConditionError(null);
163-
submit(e.currentTarget, { method: "post", action: formAction });
164-
setTimeout(() => onOpenChange(false), 100);
185+
hasHandledSuccess.current = false;
186+
fetcher.submit(e.currentTarget, { method: "post", action: formAction });
165187
}}
166188
>
167189
<input type="hidden" name="action" value="ignore" />
@@ -214,7 +236,7 @@ export function CustomIgnoreDialog({
214236
{isSubmitting ? "Ignoring…" : "Ignore error"}
215237
</Button>
216238
</DialogFooter>
217-
</Form>
239+
</fetcher.Form>
218240
</DialogContent>
219241
</Dialog>
220242
);

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors._index/route.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
55
import { IconBugFilled } from "@tabler/icons-react";
66
import { ErrorId } from "@trigger.dev/core/v3/isomorphic";
77
import { type ErrorGroupStatus } from "@trigger.dev/database";
8-
import { Suspense, useCallback, useMemo, useState, type ReactNode } from "react";
8+
import { Suspense, useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
99
import {
1010
Bar,
1111
BarChart,
@@ -49,7 +49,12 @@ import {
4949
TableRow,
5050
} from "~/components/primitives/Table";
5151
import { PopoverSectionHeader } from "~/components/primitives/Popover";
52-
import { ErrorStatusMenuItems, CustomIgnoreDialog } from "~/components/errors/ErrorStatusMenu";
52+
import {
53+
ErrorStatusMenuItems,
54+
CustomIgnoreDialog,
55+
ignoreActionToastMessage,
56+
} from "~/components/errors/ErrorStatusMenu";
57+
import { useToast } from "~/components/primitives/Toast";
5358
import TooltipPortal from "~/components/primitives/TooltipPortal";
5459
import { appliedSummary, FilterMenuProvider, TimeFilter } from "~/components/runs/v3/SharedFilters";
5560
import { $replica } from "~/db.server";
@@ -615,8 +620,17 @@ function ErrorActionsCell({
615620
projectParam: string;
616621
envParam: string;
617622
}) {
618-
const fetcher = useFetcher();
623+
const fetcher = useFetcher<{ ok?: boolean }>();
619624
const [customIgnoreOpen, setCustomIgnoreOpen] = useState(false);
625+
const toast = useToast();
626+
const pendingToast = useRef<string | undefined>();
627+
628+
useEffect(() => {
629+
if (fetcher.state === "idle" && fetcher.data?.ok && pendingToast.current) {
630+
toast.success(pendingToast.current);
631+
pendingToast.current = undefined;
632+
}
633+
}, [fetcher.state, fetcher.data, toast]);
620634

621635
const actionUrl = v3ErrorPath(
622636
{ slug: organizationSlug },
@@ -638,6 +652,7 @@ function ErrorActionsCell({
638652
taskIdentifier={errorGroup.taskIdentifier}
639653
onAction={(data) => {
640654
close();
655+
pendingToast.current = ignoreActionToastMessage(data);
641656
fetcher.submit(data, { method: "post", action: actionUrl });
642657
}}
643658
onCustomIgnore={() => {

0 commit comments

Comments
 (0)