feat: add home board for users (#505)

* feat: add home board for users

* fix: format issues

* fix: deepsource issue

* chore: address pull request feedback

* fix: typecheck issue
This commit is contained in:
Meier Lukas
2024-05-18 16:57:00 +02:00
committed by GitHub
parent dfed804f65
commit 7e339c09c8
36 changed files with 2509 additions and 62 deletions

View File

@@ -12,6 +12,7 @@ import {
integrationItems,
items,
sections,
users,
} from "@homarr/db/schema/sqlite";
import type { WidgetKind } from "@homarr/definitions";
import { getPermissionsWithParents, widgetKinds } from "@homarr/definitions";
@@ -33,14 +34,15 @@ import { throwIfActionForbiddenAsync } from "./board/board-access";
export const boardRouter = createTRPCRouter({
getAllBoards: publicProcedure.query(async ({ ctx }) => {
const userId = ctx.session?.user.id;
const permissionsOfCurrentUserWhenPresent =
await ctx.db.query.boardUserPermissions.findMany({
where: eq(boardUserPermissions.userId, ctx.session?.user.id ?? ""),
where: eq(boardUserPermissions.userId, userId ?? ""),
});
const permissionsOfCurrentUserGroupsWhenPresent =
await ctx.db.query.groupMembers.findMany({
where: eq(groupMembers.userId, ctx.session?.user.id ?? ""),
where: eq(groupMembers.userId, userId ?? ""),
with: {
group: {
with: {
@@ -60,6 +62,11 @@ export const boardRouter = createTRPCRouter({
)
.flat(),
);
const currentUserWhenPresent = await ctx.db.query.users.findFirst({
where: eq(users.id, userId ?? ""),
});
const dbBoards = await ctx.db.query.boards.findMany({
columns: {
id: true,
@@ -98,7 +105,10 @@ export const boardRouter = createTRPCRouter({
boardIds.length > 0 ? inArray(boards.id, boardIds) : undefined,
),
});
return dbBoards;
return dbBoards.map((board) => ({
...board,
isHome: currentUserWhenPresent?.homeBoardId === board.id,
}));
}),
createBoard: permissionRequiredProcedure
.requiresPermission("board-create")
@@ -160,8 +170,31 @@ export const boardRouter = createTRPCRouter({
await ctx.db.delete(boards).where(eq(boards.id, input.id));
}),
getDefaultBoard: publicProcedure.query(async ({ ctx }) => {
const boardWhere = eq(boards.name, "default");
setHomeBoard: protectedProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
await throwIfActionForbiddenAsync(
ctx,
eq(boards.id, input.id),
"board-view",
);
await ctx.db
.update(users)
.set({ homeBoardId: input.id })
.where(eq(users.id, ctx.session.user.id));
}),
getHomeBoard: publicProcedure.query(async ({ ctx }) => {
const userId = ctx.session?.user.id;
const user = userId
? await ctx.db.query.users.findFirst({
where: eq(users.id, userId),
})
: null;
const boardWhere = user?.homeBoardId
? eq(boards.id, user.homeBoardId)
: eq(boards.name, "home");
await throwIfActionForbiddenAsync(ctx, boardWhere, "board-view");
return await getFullBoardWithWhereAsync(

View File

@@ -41,6 +41,7 @@ const createRandomUserAsync = async (db: Database) => {
const userId = createId();
await db.insert(users).values({
id: userId,
homeBoardId: null,
});
return userId;
};
@@ -493,21 +494,21 @@ describe("deleteBoard should delete board", () => {
});
});
describe("getDefaultBoard should return default board", () => {
it("should return default board", async () => {
describe("getHomeBoard should return home board", () => {
it("should return home board", async () => {
// Arrange
const spy = vi.spyOn(boardAccess, "throwIfActionForbiddenAsync");
const db = createDb();
const caller = boardRouter.createCaller({ db, session: defaultSession });
const fullBoardProps = await createFullBoardAsync(db, "default");
const fullBoardProps = await createFullBoardAsync(db, "home");
// Act
const result = await caller.getDefaultBoard();
const result = await caller.getHomeBoard();
// Assert
expectInputToBeFullBoardWithName(result, {
name: "default",
name: "home",
...fullBoardProps,
});
expect(spy).toHaveBeenCalledWith(
@@ -1339,7 +1340,7 @@ describe("saveGroupBoardPermissions should save group board permissions", () =>
});
const expectInputToBeFullBoardWithName = (
input: RouterOutputs["board"]["getDefaultBoard"],
input: RouterOutputs["board"]["getHomeBoard"],
props: { name: string } & Awaited<ReturnType<typeof createFullBoardAsync>>,
) => {
expect(input.id).toBe(props.boardId);

View File

@@ -232,6 +232,7 @@ describe("editProfile shoud update user", () => {
salt: null,
password: null,
image: null,
homeBoardId: null,
});
});
@@ -274,6 +275,7 @@ describe("editProfile shoud update user", () => {
salt: null,
password: null,
image: null,
homeBoardId: null,
});
});
});
@@ -297,6 +299,7 @@ describe("delete should delete user", () => {
image: null,
password: null,
salt: null,
homeBoardId: null,
},
{
id: userToDelete,
@@ -306,6 +309,7 @@ describe("delete should delete user", () => {
image: null,
password: null,
salt: null,
homeBoardId: null,
},
{
id: createId(),
@@ -315,6 +319,7 @@ describe("delete should delete user", () => {
image: null,
password: null,
salt: null,
homeBoardId: null,
},
];