feat: add column count and is public options to board creation modal (#930)
* feat: add column count and is public options to board creation modal * test: adjust board creation test to match modified schema
This commit is contained in:
@@ -30,6 +30,8 @@ export const CreateBoardButton = ({ boardNames }: CreateBoardButtonProps) => {
|
|||||||
onSuccess: async (values) => {
|
onSuccess: async (values) => {
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
name: values.name,
|
name: values.name,
|
||||||
|
columnCount: values.columnCount,
|
||||||
|
isPublic: values.isPublic,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
boardNames,
|
boardNames,
|
||||||
|
|||||||
@@ -1,31 +1,36 @@
|
|||||||
import { Button, Group, Stack, TextInput } from "@mantine/core";
|
import { Button, Group, InputWrapper, Slider, Stack, Switch, TextInput } from "@mantine/core";
|
||||||
|
|
||||||
import { useZodForm } from "@homarr/form";
|
import { useZodForm } from "@homarr/form";
|
||||||
import { createModal } from "@homarr/modals";
|
import { createModal } from "@homarr/modals";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { validation, z } from "@homarr/validation";
|
import { validation } from "@homarr/validation";
|
||||||
import { createCustomErrorParams } from "@homarr/validation/form";
|
import { createCustomErrorParams } from "@homarr/validation/form";
|
||||||
|
|
||||||
interface InnerProps {
|
interface InnerProps {
|
||||||
boardNames: string[];
|
boardNames: string[];
|
||||||
onSuccess: ({ name }: { name: string }) => Promise<void>;
|
onSuccess: (props: { name: string; columnCount: number; isPublic: boolean }) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddBoardModal = createModal<InnerProps>(({ actions, innerProps }) => {
|
export const AddBoardModal = createModal<InnerProps>(({ actions, innerProps }) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const form = useZodForm(
|
const form = useZodForm(
|
||||||
z.object({
|
validation.board.create.refine((value) => !innerProps.boardNames.includes(value.name), {
|
||||||
name: validation.board.byName.shape.name.refine((value) => !innerProps.boardNames.includes(value), {
|
params: createCustomErrorParams("boardAlreadyExists"),
|
||||||
params: createCustomErrorParams("boardAlreadyExists"),
|
path: ["name"],
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
initialValues: {
|
initialValues: {
|
||||||
name: "",
|
name: "",
|
||||||
|
columnCount: 10,
|
||||||
|
isPublic: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const columnCountChecks = validation.board.create.shape.columnCount._def.checks;
|
||||||
|
const minColumnCount = columnCountChecks.find((check) => check.kind === "min")?.value;
|
||||||
|
const maxColumnCount = columnCountChecks.find((check) => check.kind === "max")?.value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={form.onSubmit((values) => {
|
onSubmit={form.onSubmit((values) => {
|
||||||
@@ -35,11 +40,21 @@ export const AddBoardModal = createModal<InnerProps>(({ actions, innerProps }) =
|
|||||||
>
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput label={t("board.field.name.label")} data-autofocus {...form.getInputProps("name")} />
|
<TextInput label={t("board.field.name.label")} data-autofocus {...form.getInputProps("name")} />
|
||||||
|
<InputWrapper label={t("board.field.columnCount.label")} {...form.getInputProps("columnCount")}>
|
||||||
|
<Slider min={minColumnCount} max={maxColumnCount} step={1} {...form.getInputProps("columnCount")} />
|
||||||
|
</InputWrapper>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
label={t("board.field.isPublic.label")}
|
||||||
|
description={t("board.field.isPublic.description")}
|
||||||
|
{...form.getInputProps("isPublic")}
|
||||||
|
/>
|
||||||
|
|
||||||
<Group justify="right">
|
<Group justify="right">
|
||||||
<Button onClick={actions.closeModal} variant="subtle" color="gray">
|
<Button onClick={actions.closeModal} variant="subtle" color="gray">
|
||||||
{t("common.action.cancel")}
|
{t("common.action.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button disabled={!form.isValid()} type="submit" color="teal">
|
<Button type="submit" color="teal">
|
||||||
{t("common.action.create")}
|
{t("common.action.create")}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ export const boardRouter = createTRPCRouter({
|
|||||||
await transaction.insert(boards).values({
|
await transaction.insert(boards).values({
|
||||||
id: boardId,
|
id: boardId,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
|
isPublic: input.isPublic,
|
||||||
|
columnCount: input.columnCount,
|
||||||
creatorId: ctx.session.user.id,
|
creatorId: ctx.session.user.id,
|
||||||
});
|
});
|
||||||
await transaction.insert(sections).values({
|
await transaction.insert(sections).values({
|
||||||
|
|||||||
@@ -294,12 +294,14 @@ describe("createBoard should create a new board", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await caller.createBoard({ name: "newBoard" });
|
await caller.createBoard({ name: "newBoard", columnCount: 24, isPublic: true });
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
const dbBoard = await db.query.boards.findFirst();
|
const dbBoard = await db.query.boards.findFirst();
|
||||||
expect(dbBoard).toBeDefined();
|
expect(dbBoard).toBeDefined();
|
||||||
expect(dbBoard?.name).toBe("newBoard");
|
expect(dbBoard?.name).toBe("newBoard");
|
||||||
|
expect(dbBoard?.columnCount).toBe(24);
|
||||||
|
expect(dbBoard?.isPublic).toBe(true);
|
||||||
expect(dbBoard?.creatorId).toBe(defaultCreatorId);
|
expect(dbBoard?.creatorId).toBe(defaultCreatorId);
|
||||||
|
|
||||||
const dbSection = await db.query.sections.findFirst();
|
const dbSection = await db.query.sections.findFirst();
|
||||||
@@ -314,7 +316,7 @@ describe("createBoard should create a new board", () => {
|
|||||||
const caller = boardRouter.createCaller({ db, session: defaultSession });
|
const caller = boardRouter.createCaller({ db, session: defaultSession });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const actAsync = async () => await caller.createBoard({ name: "newBoard" });
|
const actAsync = async () => await caller.createBoard({ name: "newBoard", columnCount: 12, isPublic: true });
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await expect(actAsync()).rejects.toThrowError("Permission denied");
|
await expect(actAsync()).rejects.toThrowError("Permission denied");
|
||||||
|
|||||||
@@ -1201,6 +1201,10 @@ export default {
|
|||||||
name: {
|
name: {
|
||||||
label: "Name",
|
label: "Name",
|
||||||
},
|
},
|
||||||
|
isPublic: {
|
||||||
|
label: "Public",
|
||||||
|
description: "Public boards are accessible by everyone, even without an account.",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
metaTitle: "{boardName} board",
|
metaTitle: "{boardName} board",
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const saveSchema = z.object({
|
|||||||
sections: z.array(createSectionSchema(commonItemSchema)),
|
sections: z.array(createSectionSchema(commonItemSchema)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const createSchema = z.object({ name: boardNameSchema });
|
const createSchema = z.object({ name: boardNameSchema, columnCount: z.number().min(1).max(24), isPublic: z.boolean() });
|
||||||
|
|
||||||
const permissionsSchema = z.object({
|
const permissionsSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user