diff --git a/packages/forms-collection/src/new-app/_form.tsx b/packages/forms-collection/src/new-app/_form.tsx index 96de65c63..c812831c3 100644 --- a/packages/forms-collection/src/new-app/_form.tsx +++ b/packages/forms-collection/src/new-app/_form.tsx @@ -18,7 +18,7 @@ import { findBestIconMatch } from "./icon-matcher"; type FormType = z.infer; export interface AppFormHandle { - submit: () => void; + submit: () => Promise; isDirty: () => boolean; } @@ -29,7 +29,7 @@ interface AppFormProps { submitAndCreateAnother?: string; }; initialValues?: FormType; - handleSubmit: (values: FormType, redirect: boolean, afterSuccess?: () => void) => void; + handleSubmit: (values: FormType, redirect: boolean, afterSuccess?: () => void) => void | Promise; isPending: boolean; hideButtons?: boolean; formRef?: React.Ref; @@ -74,7 +74,20 @@ export const AppForm = ({ useImperativeHandle( formRef, () => ({ - submit: () => form.onSubmit(handleSubmit)(), + submit: () => + new Promise((resolve) => { + form.onSubmit( + async (values) => { + try { + await Promise.resolve(handleSubmit(values)); + resolve(true); + } catch { + resolve(false); + } + }, + () => resolve(false), + )(); + }), isDirty: () => form.isDirty(), }), [form, handleSubmit], diff --git a/packages/widgets/src/modals/embedded-app-edit-form.tsx b/packages/widgets/src/modals/embedded-app-edit-form.tsx index 20d0125af..fe6129e1d 100644 --- a/packages/widgets/src/modals/embedded-app-edit-form.tsx +++ b/packages/widgets/src/modals/embedded-app-edit-form.tsx @@ -1,7 +1,7 @@ "use client"; import { useImperativeHandle, useRef } from "react"; -import { Alert, Center, Loader, Stack } from "@mantine/core"; +import { Alert, Button, Center, Loader, Stack, Text } from "@mantine/core"; import { IconInfoCircle } from "@tabler/icons-react"; import { clientApi } from "@homarr/api/client"; @@ -11,7 +11,7 @@ import { showErrorNotification, showSuccessNotification } from "@homarr/notifica import { useI18n } from "@homarr/translation/client"; export interface EmbeddedAppEditFormHandle { - submitIfDirty: () => void; + submitIfDirty: () => Promise; } interface EmbeddedAppEditFormProps { @@ -22,9 +22,9 @@ interface EmbeddedAppEditFormProps { export const EmbeddedAppEditForm = ({ appId, handleRef }: EmbeddedAppEditFormProps) => { const t = useI18n(); const appFormRef = useRef(null); - const { data: app, isPending: isLoadingApp } = clientApi.app.byId.useQuery({ id: appId }); + const { data: app, isPending: isLoadingApp, isError, refetch } = clientApi.app.byId.useQuery({ id: appId }); - const { mutate, isPending: isMutating } = clientApi.app.update.useMutation({ + const { mutateAsync, isPending: isMutating } = clientApi.app.update.useMutation({ onSuccess: () => { showSuccessNotification({ title: t("app.page.edit.notification.success.title"), @@ -42,16 +42,18 @@ export const EmbeddedAppEditForm = ({ appId, handleRef }: EmbeddedAppEditFormPro useImperativeHandle( handleRef, () => ({ - submitIfDirty: () => { - if (appFormRef.current?.isDirty()) { - appFormRef.current.submit(); + submitIfDirty: async () => { + if (!appFormRef.current?.isDirty()) { + return true; } + + return appFormRef.current.submit(); }, }), [], ); - if (isLoadingApp || !app) { + if (isLoadingApp) { return (
@@ -59,6 +61,17 @@ export const EmbeddedAppEditForm = ({ appId, handleRef }: EmbeddedAppEditFormPro ); } + if (isError || !app) { + return ( + + {t("app.action.select.notFound")} + + + ); + } + return ( } color="blue" variant="light"> @@ -70,8 +83,8 @@ export const EmbeddedAppEditForm = ({ appId, handleRef }: EmbeddedAppEditFormPro showBackToOverview={false} buttonLabels={{ submit: "" }} initialValues={app} - handleSubmit={(values) => { - mutate({ id: appId, ...values }); + handleSubmit={async (values) => { + await mutateAsync({ id: appId, ...values }); }} isPending={isMutating} /> diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx index 578c11ba6..b98bdb2ee 100644 --- a/packages/widgets/src/modals/widget-edit-modal.tsx +++ b/packages/widgets/src/modals/widget-edit-modal.tsx @@ -1,6 +1,6 @@ "use client"; -import { useRef, useState } from "react"; +import { useRef, useState, type FormEvent } from "react"; import { Button, Group, Stack, Tabs } from "@mantine/core"; import { schemaResolver } from "@mantine/form"; import { z } from "zod/v4"; @@ -45,6 +45,7 @@ export const WidgetEditModal = createModal>(({ actions, i const { data: session } = useSession(); const [advancedOptions, setAdvancedOptions] = useState(innerProps.value.advancedOptions); const appEditRef = useRef(null); + const [isSubmitting, setIsSubmitting] = useState(false); z.config({ customError: zodErrorMap(t), @@ -86,15 +87,29 @@ export const WidgetEditModal = createModal>(({ actions, i const appId = innerProps.appId; const showAppTab = innerProps.kind === "app" && canModifyApps && Boolean(appId); - const handleSubmit = form.onSubmit((values) => { - innerProps.onSuccessfulEdit({ - ...values, - advancedOptions, - }); - appEditRef.current?.submitIfDirty(); - actions.closeModal(); + const handleSubmit = form.onSubmit(async (values) => { + setIsSubmitting(true); + try { + const appSaved = showAppTab ? await (appEditRef.current?.submitIfDirty() ?? true) : true; + + if (!appSaved) { + return; + } + + innerProps.onSuccessfulEdit({ + ...values, + advancedOptions, + }); + actions.closeModal(); + } finally { + setIsSubmitting(false); + } }); + const onFormSubmit = (event: FormEvent) => { + handleSubmit(event); + }; + const widgetFormContent = ( {innerProps.integrationSupport && ( @@ -108,9 +123,7 @@ export const WidgetEditModal = createModal>(({ actions, i const Input = getInputForType(value.type); if ( - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition !Input || - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition value.shouldHide?.( form.values.options as never, innerProps.integrationData @@ -154,7 +167,9 @@ export const WidgetEditModal = createModal>(({ actions, i - + )} @@ -162,7 +177,7 @@ export const WidgetEditModal = createModal>(({ actions, i ); return ( -
+ {showAppTab ? ( @@ -182,7 +197,9 @@ export const WidgetEditModal = createModal>(({ actions, i - + ) : (