Skip to content

Commit cf913ec

Browse files
committed
useToast hook for success/error messages
1 parent ea3dda7 commit cf913ec

File tree

3 files changed

+36
-14
lines changed

3 files changed

+36
-14
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Fragment, useEffect, useRef, useState } from "react";
1313
import { z } from "zod";
1414
import { Button, LinkButton } from "~/components/primitives/Buttons";
1515
import { Callout, variantClasses } from "~/components/primitives/Callout";
16+
import { useToast } from "~/components/primitives/Toast";
1617
import { Fieldset } from "~/components/primitives/Fieldset";
1718
import { FormError } from "~/components/primitives/FormError";
1819
import { Header2, Header3 } from "~/components/primitives/Headers";
@@ -57,6 +58,7 @@ export function ConfigureErrorAlerts({
5758
}: ConfigureErrorAlertsProps) {
5859
const fetcher = useFetcher<{ ok?: boolean }>();
5960
const navigate = useNavigate();
61+
const toast = useToast();
6062
const location = useOptimisticLocation();
6163
const isSubmitting = fetcher.state !== "idle";
6264

@@ -80,9 +82,10 @@ export function ConfigureErrorAlerts({
8082

8183
useEffect(() => {
8284
if (fetcher.state === "idle" && fetcher.data?.ok) {
85+
toast.success("Alert settings saved");
8386
navigate(closeHref, { replace: true });
8487
}
85-
}, [fetcher.state, fetcher.data, closeHref, navigate]);
88+
}, [fetcher.state, fetcher.data, closeHref, navigate, toast]);
8689

8790
const emailFieldValues = useRef<string[]>(
8891
existingEmails.length > 0 ? [...existingEmails.map((e) => e.email), ""] : [""]

apps/webapp/app/components/primitives/Toast.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EnvelopeIcon, ExclamationCircleIcon, XMarkIcon } from "@heroicons/react/20/solid";
22
import { CheckCircleIcon } from "@heroicons/react/24/solid";
33
import { useSearchParams } from "@remix-run/react";
4-
import { useEffect } from "react";
4+
import { useEffect, useMemo } from "react";
55
import { useTypedLoaderData } from "remix-typedjson";
66
import { Toaster, toast } from "sonner";
77
import { type ToastMessageAction } from "~/models/message.server";
@@ -43,6 +43,32 @@ export function Toast() {
4343
return <Toaster />;
4444
}
4545

46+
export function useToast() {
47+
return useMemo(
48+
() => ({
49+
success(message: string, options?: { title?: string; ephemeral?: boolean }) {
50+
const ephemeral = options?.ephemeral ?? true;
51+
toast.custom(
52+
(t) => (
53+
<ToastUI variant="success" message={message} t={t as string} title={options?.title} />
54+
),
55+
{ duration: ephemeral ? defaultToastDuration : permanentToastDuration }
56+
);
57+
},
58+
error(message: string, options?: { title?: string; ephemeral?: boolean }) {
59+
const ephemeral = options?.ephemeral ?? true;
60+
toast.custom(
61+
(t) => (
62+
<ToastUI variant="error" message={message} t={t as string} title={options?.title} />
63+
),
64+
{ duration: ephemeral ? defaultToastDuration : permanentToastDuration }
65+
);
66+
},
67+
}),
68+
[]
69+
);
70+
}
71+
4672
export function ToastUI({
4773
variant,
4874
message,

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.custom.$dashboardId/route.tsx

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { Form, useNavigation } from "@remix-run/react";
55
import { IconChartHistogram, IconEdit, IconTypography } from "@tabler/icons-react";
66
import { useCallback, useEffect, useState } from "react";
77
import { typedjson, useTypedLoaderData } from "remix-typedjson";
8-
import { toast } from "sonner";
98
import { z } from "zod";
109
import { defaultChartConfig } from "~/components/code/ChartConfigPanel";
1110
import { Feedback } from "~/components/Feedback";
@@ -33,7 +32,7 @@ import {
3332
PopoverVerticalEllipseTrigger,
3433
} from "~/components/primitives/Popover";
3534
import { Sheet, SheetContent } from "~/components/primitives/SheetV3";
36-
import { ToastUI } from "~/components/primitives/Toast";
35+
import { useToast } from "~/components/primitives/Toast";
3736
import { SimpleTooltip } from "~/components/primitives/Tooltip";
3837
import { QueryEditor, type QueryEditorSaveData } from "~/components/query/QueryEditor";
3938
import { $replica, prisma } from "~/db.server";
@@ -206,7 +205,8 @@ export default function Page() {
206205
const widgetActionUrl = `/resources/orgs/${organization.slug}/projects/${project.slug}/env/${environment.slug}/dashboards/${friendlyId}/widgets`;
207206
const layoutActionUrl = widgetActionUrl;
208207

209-
// Handle sync errors by showing a toast
208+
const toast = useToast();
209+
210210
const handleSyncError = useCallback((error: Error, action: string) => {
211211
const actionMessages: Record<string, string> = {
212212
add: "Failed to add widget",
@@ -218,15 +218,8 @@ export default function Page() {
218218

219219
const message = actionMessages[action] || "Failed to save changes";
220220

221-
toast.custom((t) => (
222-
<ToastUI
223-
variant="error"
224-
message={`${message}. Your changes may not be saved.`}
225-
t={t as string}
226-
title="Sync Error"
227-
/>
228-
));
229-
}, []);
221+
toast.error(`${message}. Your changes may not be saved.`, { title: "Sync Error" });
222+
}, [toast]);
230223

231224
// Add title dialog state
232225
const [showAddTitleDialog, setShowAddTitleDialog] = useState(false);

0 commit comments

Comments
 (0)