diff --git a/apps/nextjs/src/app/[locale]/(main)/apps/_app-delete-button.tsx b/apps/nextjs/src/app/[locale]/(main)/apps/_app-delete-button.tsx new file mode 100644 index 000000000..eb34d6729 --- /dev/null +++ b/apps/nextjs/src/app/[locale]/(main)/apps/_app-delete-button.tsx @@ -0,0 +1,63 @@ +"use client"; + +import { useCallback } from "react"; + +import type { RouterOutputs } from "@homarr/api"; +import { clientApi } from "@homarr/api/client"; +import { + showErrorNotification, + showSuccessNotification, +} from "@homarr/notifications"; +import { useScopedI18n } from "@homarr/translation/client"; +import { ActionIcon, IconTrash } from "@homarr/ui"; + +import { revalidatePathAction } from "../../../revalidatePathAction"; +import { modalEvents } from "../../modals"; + +interface AppDeleteButtonProps { + app: RouterOutputs["app"]["all"][number]; +} + +export const AppDeleteButton = ({ app }: AppDeleteButtonProps) => { + const t = useScopedI18n("app.page.delete"); + const { mutate, isPending } = clientApi.app.delete.useMutation(); + + const onClick = useCallback(() => { + modalEvents.openConfirmModal({ + title: t("title"), + children: t("message", app), + onConfirm: () => { + mutate( + { id: app.id }, + { + onSuccess: () => { + showSuccessNotification({ + title: t("notification.success.title"), + message: t("notification.success.message"), + }); + void revalidatePathAction("/apps"); + }, + onError: () => { + showErrorNotification({ + title: t("notification.error.title"), + message: t("notification.error.message"), + }); + }, + }, + ); + }, + }); + }, [app, mutate, t]); + + return ( + + + + ); +}; diff --git a/apps/nextjs/src/app/[locale]/(main)/apps/_form.tsx b/apps/nextjs/src/app/[locale]/(main)/apps/_form.tsx new file mode 100644 index 000000000..ca73e3f4a --- /dev/null +++ b/apps/nextjs/src/app/[locale]/(main)/apps/_form.tsx @@ -0,0 +1,60 @@ +"use client"; + +import Link from "next/link"; + +import { useForm, zodResolver } from "@homarr/form"; +import type { TranslationFunction } from "@homarr/translation"; +import { useI18n } from "@homarr/translation/client"; +import { Button, Group, Stack, Textarea, TextInput } from "@homarr/ui"; +import type { z } from "@homarr/validation"; +import { validation } from "@homarr/validation"; + +// TODO: add icon picker +type FormType = z.infer; + +interface AppFormProps { + submitButtonTranslation: (t: TranslationFunction) => string; + initialValues?: FormType; + handleSubmit: (values: FormType) => void; + isPending: boolean; +} + +export const AppForm = (props: AppFormProps) => { + const { submitButtonTranslation, handleSubmit, initialValues, isPending } = + props; + const t = useI18n(); + + const form = useForm({ + initialValues: initialValues ?? { + name: "", + description: "", + iconUrl: "", + href: "", + }, + validate: zodResolver(validation.app.manage), + }); + + return ( +
+ + + +