"use client"; import { useRef, useState } from "react"; import { Button, Group, Stack, Tabs } from "@mantine/core"; import { schemaResolver } from "@mantine/form"; import { z } from "zod/v4"; import { objectEntries } from "@homarr/common"; import { useSession } from "@homarr/auth/client"; import type { WidgetKind } from "@homarr/definitions"; import { createModal, useModalAction } from "@homarr/modals"; import type { SettingsContextProps } from "@homarr/settings/creator"; import { useI18n } from "@homarr/translation/client"; import { zodErrorMap } from "@homarr/validation/form/i18n"; import { widgetImports } from ".."; import { getInputForType } from "../_inputs"; import { FormProvider, useForm } from "../_inputs/form"; import type { BoardItemAdvancedOptions } from "../../../validation/src/shared"; import type { OptionsBuilderResult } from "../options"; import type { IntegrationSelectOption } from "../widget-integration-select"; import { WidgetIntegrationSelect } from "../widget-integration-select"; import { WidgetAdvancedOptionsModal } from "./widget-advanced-options-modal"; import type { EmbeddedAppEditFormHandle } from "./embedded-app-edit-form"; import { EmbeddedAppEditForm } from "./embedded-app-edit-form"; export interface WidgetEditModalState { options: Record; integrationIds: string[]; advancedOptions: BoardItemAdvancedOptions; } interface ModalProps { kind: TSort; value: WidgetEditModalState; onSuccessfulEdit: (value: WidgetEditModalState) => void; integrationData: IntegrationSelectOption[]; integrationSupport: boolean; settings: SettingsContextProps; appId?: string; } export const WidgetEditModal = createModal>(({ actions, innerProps }) => { const t = useI18n(); const { data: session } = useSession(); const [advancedOptions, setAdvancedOptions] = useState(innerProps.value.advancedOptions); const appEditRef = useRef(null); z.config({ customError: zodErrorMap(t), }); const { definition } = widgetImports[innerProps.kind]; const options = definition.createOptions(innerProps.settings) as Record; const form = useForm({ mode: "controlled", initialValues: innerProps.value, validate: schemaResolver( z.object({ options: z.object( objectEntries(options).reduce( (acc, [key, value]: [string, { type: string; validate?: z.ZodType }]) => { if (value.validate) { acc[key] = value.type === "multiText" ? z.array(value.validate).optional() : value.validate; } return acc; }, {} as Record>, ), ), integrationIds: z.array(z.string()), advancedOptions: z.object({ customCssClasses: z.array(z.string()), borderColor: z.string(), }), }), { sync: true }, ), validateInputOnBlur: true, validateInputOnChange: true, }); const { openModal } = useModalAction(WidgetAdvancedOptionsModal); const canModifyApps = session?.user.permissions.includes("app-modify-all") ?? false; 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 widgetFormContent = ( {innerProps.integrationSupport && ( )} {Object.entries(options).map(([key, value]) => { 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 .filter(({ id }) => form.values.integrationIds.includes(id)) .map(({ kind }) => kind), ) ) { return null; } return ( ); })} {!showAppTab && ( )} ); return (
{showAppTab ? ( {t("item.edit.tab.widget")} {t("item.edit.tab.app")} {widgetFormContent} {appId && } ) : ( widgetFormContent )}
); }).withOptions({ keepMounted: true, defaultTitle(t) { return t("item.edit.title"); }, size: "lg", });