From 8d5984c58a6ec6ee04cce49d51ab9aee53577bf9 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Mon, 4 Mar 2024 22:13:40 +0100 Subject: [PATCH] feat: Add apps crud (#174) * wip: add apps crud * wip: add edit for apps * feat: add apps crud * fix: color of icon for no app results wrong * ci: fix lint issues * test: add unit tests for app crud * ci: fix format issue * fix: missing rename in edit form * fix: missing callback deepsource issues --- .../(main)/apps/_app-delete-button.tsx | 63 ++ .../src/app/[locale]/(main)/apps/_form.tsx | 60 ++ .../(main)/apps/edit/[id]/_app-edit-form.tsx | 68 ++ .../[locale]/(main)/apps/edit/[id]/page.tsx | 23 + .../(main)/apps/new/_app-new-form.tsx | 59 ++ .../src/app/[locale]/(main)/apps/new/page.tsx | 14 + .../src/app/[locale]/(main)/apps/page.tsx | 117 +++ packages/api/src/root.ts | 2 + packages/api/src/router/app.ts | 71 ++ packages/api/src/router/test/app.spec.ts | 209 +++++ packages/db/migrations/0001_slim_swarm.sql | 7 + .../db/migrations/meta/0001_snapshot.json | 722 ++++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/schema/sqlite.ts | 8 + packages/translation/src/lang/en.ts | 51 ++ packages/validation/src/app.ts | 18 + packages/validation/src/index.ts | 2 + 17 files changed, 1501 insertions(+) create mode 100644 apps/nextjs/src/app/[locale]/(main)/apps/_app-delete-button.tsx create mode 100644 apps/nextjs/src/app/[locale]/(main)/apps/_form.tsx create mode 100644 apps/nextjs/src/app/[locale]/(main)/apps/edit/[id]/_app-edit-form.tsx create mode 100644 apps/nextjs/src/app/[locale]/(main)/apps/edit/[id]/page.tsx create mode 100644 apps/nextjs/src/app/[locale]/(main)/apps/new/_app-new-form.tsx create mode 100644 apps/nextjs/src/app/[locale]/(main)/apps/new/page.tsx create mode 100644 apps/nextjs/src/app/[locale]/(main)/apps/page.tsx create mode 100644 packages/api/src/router/app.ts create mode 100644 packages/api/src/router/test/app.spec.ts create mode 100644 packages/db/migrations/0001_slim_swarm.sql create mode 100644 packages/db/migrations/meta/0001_snapshot.json create mode 100644 packages/validation/src/app.ts 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 ( +
+ + + +