feat: add i18n translated form errors (#509)
This commit is contained in:
@@ -4,7 +4,7 @@ import { useRouter } from "next/navigation";
|
||||
import { Button, PasswordInput, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
@@ -24,18 +24,17 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
|
||||
const t = useScopedI18n("user");
|
||||
const router = useRouter();
|
||||
const { mutate, isPending } = clientApi.user.register.useMutation();
|
||||
const form = useForm<FormType>({
|
||||
validate: zodResolver(validation.user.registration),
|
||||
const form = useZodForm(validation.user.registration, {
|
||||
initialValues: {
|
||||
username: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
},
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
|
||||
const handleSubmit = (values: FormType) => {
|
||||
const handleSubmit = (
|
||||
values: z.infer<typeof validation.user.registration>,
|
||||
) => {
|
||||
mutate(
|
||||
{
|
||||
...values,
|
||||
@@ -88,5 +87,3 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
type FormType = z.infer<typeof validation.user.registration>;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { IconAlertTriangle } from "@tabler/icons-react";
|
||||
|
||||
import { signIn } from "@homarr/auth/client";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
@@ -27,15 +27,16 @@ export const LoginForm = () => {
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
const form = useForm<FormType>({
|
||||
validate: zodResolver(validation.user.signIn),
|
||||
const form = useZodForm(validation.user.signIn, {
|
||||
initialValues: {
|
||||
name: "",
|
||||
password: "",
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmitAsync = async (values: FormType) => {
|
||||
const handleSubmitAsync = async (
|
||||
values: z.infer<typeof validation.user.signIn>,
|
||||
) => {
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
await signIn("credentials", {
|
||||
@@ -92,5 +93,3 @@ export const LoginForm = () => {
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
type FormType = z.infer<typeof validation.user.signIn>;
|
||||
|
||||
@@ -7,11 +7,12 @@ import {
|
||||
backgroundImageRepeats,
|
||||
backgroundImageSizes,
|
||||
} from "@homarr/definitions";
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import type { TranslationObject } from "@homarr/translation";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import type { SelectItemWithDescriptionBadge } from "@homarr/ui";
|
||||
import { SelectWithDescriptionBadge } from "@homarr/ui";
|
||||
import { validation } from "@homarr/validation";
|
||||
|
||||
import type { Board } from "../../_types";
|
||||
import { useSavePartialSettingsMutation } from "./_shared";
|
||||
@@ -23,7 +24,7 @@ export const BackgroundSettingsContent = ({ board }: Props) => {
|
||||
const t = useI18n();
|
||||
const { mutate: savePartialSettings, isPending } =
|
||||
useSavePartialSettingsMutation(board);
|
||||
const form = useForm({
|
||||
const form = useZodForm(validation.board.savePartialSettings, {
|
||||
initialValues: {
|
||||
backgroundImageUrl: board.backgroundImageUrl ?? "",
|
||||
backgroundImageAttachment: board.backgroundImageAttachment,
|
||||
|
||||
@@ -17,8 +17,9 @@ import {
|
||||
} from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { validation } from "@homarr/validation";
|
||||
|
||||
import type { Board } from "../../_types";
|
||||
import { generateColors } from "../../(content)/_theme";
|
||||
@@ -33,7 +34,7 @@ const hexRegex = /^#[0-9a-fA-F]{6}$/;
|
||||
const progressPercentageLabel = (value: number) => `${value}%`;
|
||||
|
||||
export const ColorSettingsContent = ({ board }: Props) => {
|
||||
const form = useForm({
|
||||
const form = useZodForm(validation.board.savePartialSettings, {
|
||||
initialValues: {
|
||||
primaryColor: board.primaryColor,
|
||||
secondaryColor: board.secondaryColor,
|
||||
@@ -114,15 +115,16 @@ export const ColorSettingsContent = ({ board }: Props) => {
|
||||
};
|
||||
|
||||
interface ColorsPreviewProps {
|
||||
previewColor: string;
|
||||
previewColor: string | undefined;
|
||||
}
|
||||
|
||||
const ColorsPreview = ({ previewColor }: ColorsPreviewProps) => {
|
||||
const theme = useMantineTheme();
|
||||
|
||||
const colors = hexRegex.test(previewColor)
|
||||
? generateColors(previewColor)
|
||||
: generateColors("#000000");
|
||||
const colors =
|
||||
previewColor && hexRegex.test(previewColor)
|
||||
? generateColors(previewColor)
|
||||
: generateColors("#000000");
|
||||
|
||||
return (
|
||||
<Group gap={0} wrap="nowrap">
|
||||
|
||||
@@ -17,8 +17,9 @@ import {
|
||||
} from "@mantine/hooks";
|
||||
import { IconAlertTriangle } from "@tabler/icons-react";
|
||||
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { validation } from "@homarr/validation";
|
||||
|
||||
import type { Board } from "../../_types";
|
||||
import { useUpdateBoard } from "../../(content)/_client";
|
||||
@@ -38,20 +39,30 @@ export const GeneralSettingsContent = ({ board }: Props) => {
|
||||
|
||||
const { mutate: savePartialSettings, isPending } =
|
||||
useSavePartialSettingsMutation(board);
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
pageTitle: board.pageTitle ?? "",
|
||||
logoImageUrl: board.logoImageUrl ?? "",
|
||||
metaTitle: board.metaTitle ?? "",
|
||||
faviconImageUrl: board.faviconImageUrl ?? "",
|
||||
const form = useZodForm(
|
||||
validation.board.savePartialSettings
|
||||
.pick({
|
||||
pageTitle: true,
|
||||
logoImageUrl: true,
|
||||
metaTitle: true,
|
||||
faviconImageUrl: true,
|
||||
})
|
||||
.required(),
|
||||
{
|
||||
initialValues: {
|
||||
pageTitle: board.pageTitle ?? "",
|
||||
logoImageUrl: board.logoImageUrl ?? "",
|
||||
metaTitle: board.metaTitle ?? "",
|
||||
faviconImageUrl: board.faviconImageUrl ?? "",
|
||||
},
|
||||
onValuesChange({ pageTitle }) {
|
||||
updateBoard((previous) => ({
|
||||
...previous,
|
||||
pageTitle,
|
||||
}));
|
||||
},
|
||||
},
|
||||
onValuesChange({ pageTitle }) {
|
||||
updateBoard((previous) => ({
|
||||
...previous,
|
||||
pageTitle,
|
||||
}));
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
const metaTitleStatus = useMetaTitlePreview(form.values.metaTitle);
|
||||
const faviconStatus = useFaviconPreview(form.values.faviconImageUrl);
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
import { Button, Grid, Group, Input, Slider, Stack } from "@mantine/core";
|
||||
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { validation } from "@homarr/validation";
|
||||
|
||||
import type { Board } from "../../_types";
|
||||
import { useSavePartialSettingsMutation } from "./_shared";
|
||||
@@ -15,11 +16,14 @@ export const LayoutSettingsContent = ({ board }: Props) => {
|
||||
const t = useI18n();
|
||||
const { mutate: savePartialSettings, isPending } =
|
||||
useSavePartialSettingsMutation(board);
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
columnCount: board.columnCount,
|
||||
const form = useZodForm(
|
||||
validation.board.savePartialSettings.pick({ columnCount: true }).required(),
|
||||
{
|
||||
initialValues: {
|
||||
columnCount: board.columnCount,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
return (
|
||||
<form
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useRouter } from "next/navigation";
|
||||
import { Button, PasswordInput, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
@@ -18,10 +18,7 @@ export const InitUserForm = () => {
|
||||
const t = useScopedI18n("user");
|
||||
const { mutateAsync, error, isPending } =
|
||||
clientApi.user.initUser.useMutation();
|
||||
const form = useForm<FormType>({
|
||||
validate: zodResolver(validation.user.init),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
const form = useZodForm(validation.user.init, {
|
||||
initialValues: {
|
||||
username: "",
|
||||
password: "",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import Link from "next/link";
|
||||
import { Button, Group, Stack, Textarea, TextInput } from "@mantine/core";
|
||||
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import type { TranslationFunction } from "@homarr/translation";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import type { z } from "@homarr/validation";
|
||||
@@ -25,14 +25,13 @@ export const AppForm = (props: AppFormProps) => {
|
||||
props;
|
||||
const t = useI18n();
|
||||
|
||||
const form = useForm({
|
||||
const form = useZodForm(validation.app.manage, {
|
||||
initialValues: initialValues ?? {
|
||||
name: "",
|
||||
description: "",
|
||||
iconUrl: "",
|
||||
href: "",
|
||||
},
|
||||
validate: zodResolver(validation.app.manage),
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -41,9 +40,7 @@ export const AppForm = (props: AppFormProps) => {
|
||||
<TextInput {...form.getInputProps("name")} withAsterisk label="Name" />
|
||||
<IconPicker
|
||||
initialValue={initialValues?.iconUrl}
|
||||
onChange={(iconUrl) => {
|
||||
form.setFieldValue("iconUrl", iconUrl);
|
||||
}}
|
||||
{...form.getInputProps("iconUrl")}
|
||||
/>
|
||||
<Textarea {...form.getInputProps("description")} label="Description" />
|
||||
<TextInput {...form.getInputProps("href")} label="URL" />
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useI18n } from "@homarr/translation/client";
|
||||
import { integrationSecretIcons } from "./_integration-secret-icons";
|
||||
|
||||
interface IntegrationSecretInputProps {
|
||||
withAsterisk?: boolean;
|
||||
label?: string;
|
||||
kind: IntegrationSecretKind;
|
||||
value?: string;
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
getAllSecretKindOptions,
|
||||
getDefaultSecretKinds,
|
||||
} from "@homarr/definitions";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { useConfirmModal } from "@homarr/modals";
|
||||
import {
|
||||
showErrorNotification,
|
||||
@@ -55,9 +55,8 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const form = useForm<FormType>({
|
||||
const form = useZodForm(validation.integration.update.omit({ id: true }), {
|
||||
initialValues: initialFormValues,
|
||||
validate: zodResolver(validation.integration.update.omit({ id: true })),
|
||||
onValuesChange,
|
||||
});
|
||||
const { mutateAsync, isPending } = clientApi.integration.update.useMutation();
|
||||
@@ -103,11 +102,13 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
|
||||
<TestConnectionNoticeAlert />
|
||||
|
||||
<TextInput
|
||||
withAsterisk
|
||||
label={t("integration.field.name.label")}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
withAsterisk
|
||||
label={t("integration.field.url.label")}
|
||||
{...form.getInputProps("url")}
|
||||
/>
|
||||
|
||||
@@ -19,7 +19,7 @@ import type {
|
||||
} from "@homarr/definitions";
|
||||
import { getAllSecretKindOptions } from "@homarr/definitions";
|
||||
import type { UseFormReturnType } from "@homarr/form";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
@@ -60,9 +60,8 @@ export const NewIntegrationForm = ({
|
||||
initialFormValue: initialFormValues,
|
||||
});
|
||||
const router = useRouter();
|
||||
const form = useForm<FormType>({
|
||||
const form = useZodForm(validation.integration.create.omit({ kind: true }), {
|
||||
initialValues: initialFormValues,
|
||||
validate: zodResolver(validation.integration.create.omit({ kind: true })),
|
||||
onValuesChange,
|
||||
});
|
||||
const { mutateAsync, isPending } = clientApi.integration.create.useMutation();
|
||||
@@ -100,11 +99,13 @@ export const NewIntegrationForm = ({
|
||||
<TestConnectionNoticeAlert />
|
||||
|
||||
<TextInput
|
||||
withAsterisk
|
||||
label={t("integration.field.name.label")}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
withAsterisk
|
||||
label={t("integration.field.url.label")}
|
||||
{...form.getInputProps("url")}
|
||||
/>
|
||||
@@ -119,6 +120,7 @@ export const NewIntegrationForm = ({
|
||||
)}
|
||||
{form.values.secrets.map(({ kind }, index) => (
|
||||
<IntegrationSecretInput
|
||||
withAsterisk
|
||||
key={kind}
|
||||
kind={kind}
|
||||
{...form.getInputProps(`secrets.${index}.value`)}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button, Group, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import type { RouterInputs, RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
@@ -38,14 +38,11 @@ export const UserProfileForm = ({ user }: UserProfileFormProps) => {
|
||||
});
|
||||
},
|
||||
});
|
||||
const form = useForm({
|
||||
const form = useZodForm(validation.user.editProfile.omit({ id: true }), {
|
||||
initialValues: {
|
||||
name: user.name ?? "",
|
||||
email: user.email ?? "",
|
||||
},
|
||||
validate: zodResolver(validation.user.editProfile.omit({ id: true })),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button, Fieldset, Group, PasswordInput, Stack } from "@mantine/core";
|
||||
import type { RouterInputs, RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useSession } from "@homarr/auth/client";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
@@ -37,15 +37,13 @@ export const ChangePasswordForm = ({ user }: ChangePasswordFormProps) => {
|
||||
});
|
||||
},
|
||||
});
|
||||
const form = useForm<FormType>({
|
||||
const form = useZodForm(validation.user.changePassword, {
|
||||
initialValues: {
|
||||
previousPassword: "",
|
||||
/* Require previous password if the current user want's to change his password */
|
||||
previousPassword: session?.user.id === user.id ? "" : "_",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
},
|
||||
validate: zodResolver(validation.user.changePassword),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
|
||||
const handleSubmit = (values: FormType) => {
|
||||
|
||||
@@ -14,9 +14,10 @@ import {
|
||||
import { IconUserCheck } from "@tabler/icons-react";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { validation, z } from "@homarr/validation";
|
||||
import { createCustomErrorParams } from "@homarr/validation/form";
|
||||
|
||||
import { StepperNavigationComponent } from "./stepper-navigation.component";
|
||||
|
||||
@@ -40,40 +41,36 @@ export const UserCreateStepperComponent = () => {
|
||||
|
||||
const { mutateAsync, isPending } = clientApi.user.create.useMutation();
|
||||
|
||||
const generalForm = useForm({
|
||||
initialValues: {
|
||||
username: "",
|
||||
email: undefined,
|
||||
const generalForm = useZodForm(
|
||||
z.object({
|
||||
username: z.string().min(1),
|
||||
email: z.string().email().or(z.string().length(0).optional()),
|
||||
}),
|
||||
{
|
||||
initialValues: {
|
||||
username: "",
|
||||
email: "",
|
||||
},
|
||||
},
|
||||
validate: zodResolver(
|
||||
z.object({
|
||||
username: z.string().min(1),
|
||||
email: z.string().email().or(z.string().length(0).optional()),
|
||||
}),
|
||||
),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
);
|
||||
|
||||
const securityForm = useForm({
|
||||
initialValues: {
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
const securityForm = useZodForm(
|
||||
z
|
||||
.object({
|
||||
password: validation.user.password,
|
||||
confirmPassword: z.string(),
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
path: ["confirmPassword"],
|
||||
params: createCustomErrorParams("passwordsDoNotMatch"),
|
||||
}),
|
||||
{
|
||||
initialValues: {
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
},
|
||||
},
|
||||
validate: zodResolver(
|
||||
z
|
||||
.object({
|
||||
password: validation.user.password,
|
||||
confirmPassword: z.string(),
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
path: ["confirmPassword"],
|
||||
message: "Passwords do not match",
|
||||
}),
|
||||
),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
);
|
||||
|
||||
const allForms = useMemo(
|
||||
() => [generalForm, securityForm],
|
||||
|
||||
@@ -4,12 +4,13 @@ import { useCallback } from "react";
|
||||
import { Button, Group, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
} from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { validation } from "@homarr/validation";
|
||||
|
||||
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
|
||||
|
||||
@@ -23,7 +24,7 @@ interface RenameGroupFormProps {
|
||||
export const RenameGroupForm = ({ group }: RenameGroupFormProps) => {
|
||||
const t = useI18n();
|
||||
const { mutate, isPending } = clientApi.group.updateGroup.useMutation();
|
||||
const form = useForm<FormType>({
|
||||
const form = useZodForm(validation.group.update.pick({ name: true }), {
|
||||
initialValues: {
|
||||
name: group.name,
|
||||
},
|
||||
|
||||
@@ -4,13 +4,14 @@ import { useCallback } from "react";
|
||||
import { Button, Group, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { createModal, useModalAction } from "@homarr/modals";
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
} from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { validation } from "@homarr/validation";
|
||||
|
||||
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
|
||||
|
||||
@@ -32,7 +33,7 @@ export const AddGroup = () => {
|
||||
const AddGroupModal = createModal<void>(({ actions }) => {
|
||||
const t = useI18n();
|
||||
const { mutate, isPending } = clientApi.group.createGroup.useMutation();
|
||||
const form = useForm({
|
||||
const form = useZodForm(validation.group.create, {
|
||||
initialValues: {
|
||||
name: "",
|
||||
},
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
import { Button, Group, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { createModal } from "@homarr/modals";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import type { validation, z } from "@homarr/validation";
|
||||
import type { z } from "@homarr/validation";
|
||||
import { validation } from "@homarr/validation";
|
||||
|
||||
interface InnerProps {
|
||||
id: string;
|
||||
@@ -26,7 +27,7 @@ export const BoardRenameModal = createModal<InnerProps>(
|
||||
void utils.board.getDefaultBoard.invalidate();
|
||||
},
|
||||
});
|
||||
const form = useForm<FormType>({
|
||||
const form = useZodForm(validation.board.rename.omit({ id: true }), {
|
||||
initialValues: {
|
||||
name: innerProps.previousName,
|
||||
},
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Button, Group, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import { useForm } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { createModal } from "@homarr/modals";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { z } from "@homarr/validation";
|
||||
|
||||
interface Category {
|
||||
id: string;
|
||||
@@ -18,7 +19,7 @@ interface InnerProps {
|
||||
export const CategoryEditModal = createModal<InnerProps>(
|
||||
({ actions, innerProps }) => {
|
||||
const t = useI18n();
|
||||
const form = useForm({
|
||||
const form = useZodForm(z.object({ name: z.string().min(1) }), {
|
||||
initialValues: {
|
||||
name: innerProps.category.name,
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { FocusEventHandler } from "react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Combobox,
|
||||
@@ -15,9 +16,18 @@ import { useScopedI18n } from "@homarr/translation/client";
|
||||
interface IconPickerProps {
|
||||
initialValue?: string;
|
||||
onChange: (iconUrl: string) => void;
|
||||
error?: string | null;
|
||||
onFocus?: FocusEventHandler;
|
||||
onBlur?: FocusEventHandler;
|
||||
}
|
||||
|
||||
export const IconPicker = ({ initialValue, onChange }: IconPickerProps) => {
|
||||
export const IconPicker = ({
|
||||
initialValue,
|
||||
onChange,
|
||||
error,
|
||||
onFocus,
|
||||
onBlur,
|
||||
}: IconPickerProps) => {
|
||||
const [value, setValue] = useState<string>(initialValue ?? "");
|
||||
const [search, setSearch] = useState(initialValue ?? "");
|
||||
|
||||
@@ -76,13 +86,18 @@ export const IconPicker = ({ initialValue, onChange }: IconPickerProps) => {
|
||||
setSearch(event.currentTarget.value);
|
||||
}}
|
||||
onClick={() => combobox.openDropdown()}
|
||||
onFocus={() => combobox.openDropdown()}
|
||||
onBlur={() => {
|
||||
onFocus={(event) => {
|
||||
onFocus?.(event);
|
||||
combobox.openDropdown();
|
||||
}}
|
||||
onBlur={(event) => {
|
||||
onBlur?.(event);
|
||||
combobox.closeDropdown();
|
||||
setSearch(value || "");
|
||||
}}
|
||||
rightSectionPointerEvents="none"
|
||||
withAsterisk
|
||||
error={error}
|
||||
label="Icon URL"
|
||||
/>
|
||||
</Combobox.Target>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Button, Group, Stack, TextInput } from "@mantine/core";
|
||||
import { boardSchemas } from "node_modules/@homarr/validation/src/board";
|
||||
|
||||
import { useForm, zodResolver } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { createModal } from "@homarr/modals";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { z } from "@homarr/validation";
|
||||
import { createCustomErrorParams } from "@homarr/validation/form";
|
||||
|
||||
interface InnerProps {
|
||||
boardNames: string[];
|
||||
@@ -14,20 +15,21 @@ interface InnerProps {
|
||||
export const AddBoardModal = createModal<InnerProps>(
|
||||
({ actions, innerProps }) => {
|
||||
const t = useI18n();
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
name: "",
|
||||
const form = useZodForm(
|
||||
z.object({
|
||||
name: boardSchemas.byName.shape.name.refine(
|
||||
(value) => !innerProps.boardNames.includes(value),
|
||||
{
|
||||
params: createCustomErrorParams("boardAlreadyExists"),
|
||||
},
|
||||
),
|
||||
}),
|
||||
{
|
||||
initialValues: {
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
validate: zodResolver(
|
||||
z.object({
|
||||
name: boardSchemas.byName.shape.name.refine(
|
||||
(value) => !innerProps.boardNames.includes(value),
|
||||
),
|
||||
}),
|
||||
),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
);
|
||||
|
||||
return (
|
||||
<form
|
||||
|
||||
Reference in New Issue
Block a user