import { CheckIcon } from "@heroicons/react/20/solid"; import { IconAlarmSnooze as IconAlarmSnoozeBase, IconArrowBackUp as IconArrowBackUpBase, IconBugOff as IconBugOffBase, } from "@tabler/icons-react"; import { useEffect, useRef, useState } from "react"; import { type ErrorGroupStatus } from "@trigger.dev/database"; import { useFetcher } from "@remix-run/react"; import { Button } from "~/components/primitives/Buttons"; import { useToast } from "~/components/primitives/Toast"; import { FormError } from "~/components/primitives/FormError"; import { Input } from "~/components/primitives/Input"; import { InputGroup } from "~/components/primitives/InputGroup"; import { Label } from "~/components/primitives/Label"; import { PopoverMenuItem } from "~/components/primitives/Popover"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "~/components/primitives/Dialog"; const AlarmSnoozeIcon = ({ className }: { className?: string }) => ( ); const ArrowBackUpIcon = ({ className }: { className?: string }) => ( ); const BugOffIcon = ({ className }: { className?: string }) => ( ); export function statusActionToastMessage(data: Record): string { switch (data.action) { case "resolve": return "Error marked as resolved"; case "unresolve": return "Error marked as unresolved"; case "ignore": { const duration = data.duration ? Number(data.duration) : undefined; if (!duration) return "Error ignored indefinitely"; const hours = duration / (60 * 60 * 1000); if (hours < 24) return `Error ignored for ${hours} ${hours === 1 ? "hour" : "hours"}`; const days = hours / 24; return `Error ignored for ${days} ${days === 1 ? "day" : "days"}`; } default: return "Error status updated"; } } export function ErrorStatusMenuItems({ status, taskIdentifier, onAction, onCustomIgnore, }: { status: ErrorGroupStatus; taskIdentifier: string; onAction: (data: Record) => void; onCustomIgnore: () => void; }) { return ( <> {status === "UNRESOLVED" && ( <> onAction({ taskIdentifier, action: "resolve" })} /> onAction({ taskIdentifier, action: "ignore", duration: String(60 * 60 * 1000), }) } /> onAction({ taskIdentifier, action: "ignore", duration: String(24 * 60 * 60 * 1000), }) } /> onAction({ taskIdentifier, action: "ignore" })} /> )} {status === "IGNORED" && ( <> onAction({ taskIdentifier, action: "resolve" })} /> onAction({ taskIdentifier, action: "unresolve" })} /> )} {status === "RESOLVED" && ( onAction({ taskIdentifier, action: "unresolve" })} /> )} ); } export function CustomIgnoreDialog({ open, onOpenChange, taskIdentifier, formAction, }: { open: boolean; onOpenChange: (open: boolean) => void; taskIdentifier: string; formAction?: string; }) { const fetcher = useFetcher<{ ok?: boolean }>(); const isSubmitting = fetcher.state !== "idle"; const [conditionError, setConditionError] = useState(null); const toast = useToast(); const hasHandledSuccess = useRef(false); useEffect(() => { if (fetcher.state === "idle" && fetcher.data?.ok && !hasHandledSuccess.current) { hasHandledSuccess.current = true; toast.success("Error ignored with custom condition"); onOpenChange(false); } }, [fetcher.state, fetcher.data, onOpenChange, toast]); return ( Custom ignore condition { e.preventDefault(); const formData = new FormData(e.currentTarget); const rate = formData.get("occurrenceRate")?.toString().trim(); const total = formData.get("totalOccurrences")?.toString().trim(); if (!rate && !total) { setConditionError("At least one unignore condition is required"); return; } setConditionError(null); hasHandledSuccess.current = false; fetcher.submit(e.currentTarget, { method: "post", action: formAction }); }} >
conditionError && setConditionError(null)} /> conditionError && setConditionError(null)} /> {conditionError && {conditionError}}
); }