feat: add board favourites with animations (#318)
* feat: add board favorites with animations * feat: add board favorites functionality with admin-only access * refactor: make board favorites user-specific via junction table * fix: assertUserInWorkspace * Update pnpm lockfile to match package.json * chore: clean up migrations --------- Co-authored-by: Henry <henry_ball@hotmail.co.uk>
This commit is contained in:
committed by
GitHub
parent
ec37f5480a
commit
ef5bf87fdf
@@ -26,6 +26,7 @@ import {
|
||||
comments,
|
||||
labels,
|
||||
lists,
|
||||
userBoardFavorites,
|
||||
workspaceMembers,
|
||||
} from "@kan/db/schema";
|
||||
import { generateUID } from "@kan/shared/utils";
|
||||
@@ -39,17 +40,24 @@ export const getCount = async (db: dbClient) => {
|
||||
return result[0]?.count ?? 0;
|
||||
};
|
||||
|
||||
export const getAllByWorkspaceId = (
|
||||
export const getAllByWorkspaceId = async (
|
||||
db: dbClient,
|
||||
workspaceId: number,
|
||||
userId: string,
|
||||
opts?: { type?: "regular" | "template" },
|
||||
) => {
|
||||
return db.query.boards.findMany({
|
||||
const boardsData = await db.query.boards.findMany({
|
||||
columns: {
|
||||
publicId: true,
|
||||
name: true,
|
||||
},
|
||||
with: {
|
||||
userFavorites: {
|
||||
where: eq(userBoardFavorites.userId, userId),
|
||||
columns: {
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
lists: {
|
||||
columns: {
|
||||
publicId: true,
|
||||
@@ -72,6 +80,21 @@ export const getAllByWorkspaceId = (
|
||||
opts?.type ? eq(boards.type, opts.type) : undefined,
|
||||
),
|
||||
});
|
||||
|
||||
// Transform and sort: favorites first, then alphabetically
|
||||
return boardsData
|
||||
.map((board) => ({
|
||||
...board,
|
||||
favorite: board.userFavorites.length > 0,
|
||||
userFavorites: undefined,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
// Sort favorites first
|
||||
if (a.favorite && !b.favorite) return -1;
|
||||
if (!a.favorite && b.favorite) return 1;
|
||||
// Then alphabetically by name
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
};
|
||||
|
||||
export const getIdByPublicId = async (db: dbClient, boardPublicId: string) => {
|
||||
@@ -122,6 +145,7 @@ const buildDueDateWhere = (filters: DueDateFilter[]) => {
|
||||
export const getByPublicId = async (
|
||||
db: dbClient,
|
||||
boardPublicId: string,
|
||||
userId: string,
|
||||
filters: {
|
||||
members: string[];
|
||||
labels: string[];
|
||||
@@ -173,6 +197,12 @@ export const getByPublicId = async (
|
||||
visibility: true,
|
||||
},
|
||||
with: {
|
||||
userFavorites: {
|
||||
where: eq(userBoardFavorites.userId, userId),
|
||||
columns: {
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
workspace: {
|
||||
columns: {
|
||||
publicId: true,
|
||||
@@ -325,6 +355,8 @@ export const getByPublicId = async (
|
||||
|
||||
const formattedResult = {
|
||||
...board,
|
||||
favorite: board.userFavorites.length > 0,
|
||||
userFavorites: undefined,
|
||||
lists: board.lists.map((list) => ({
|
||||
...list,
|
||||
cards: list.cards.map((card) => ({
|
||||
@@ -924,3 +956,34 @@ export const createFromSnapshot = async (
|
||||
return newBoard;
|
||||
});
|
||||
};
|
||||
|
||||
export const addUserFavorite = async (
|
||||
db: dbClient,
|
||||
userId: string,
|
||||
boardId: number,
|
||||
) => {
|
||||
return db
|
||||
.insert(userBoardFavorites)
|
||||
.values({
|
||||
userId,
|
||||
boardId,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
.returning();
|
||||
};
|
||||
|
||||
export const removeUserFavorite = async (
|
||||
db: dbClient,
|
||||
userId: string,
|
||||
boardId: number,
|
||||
) => {
|
||||
return db
|
||||
.delete(userBoardFavorites)
|
||||
.where(
|
||||
and(
|
||||
eq(userBoardFavorites.userId, userId),
|
||||
eq(userBoardFavorites.boardId, boardId)
|
||||
)
|
||||
)
|
||||
.returning();
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ilike,
|
||||
inArray,
|
||||
isNull,
|
||||
asc,
|
||||
or,
|
||||
sql,
|
||||
} from "drizzle-orm";
|
||||
@@ -250,6 +251,7 @@ export const getBySlugWithBoards = (db: dbClient, workspaceSlug: string) => {
|
||||
name: true,
|
||||
},
|
||||
where: and(isNull(boards.deletedAt), eq(boards.visibility, "public")),
|
||||
orderBy: [asc(boards.name)]
|
||||
},
|
||||
},
|
||||
where: and(
|
||||
|
||||
@@ -2,9 +2,11 @@ import { relations, sql } from "drizzle-orm";
|
||||
import {
|
||||
bigint,
|
||||
bigserial,
|
||||
boolean,
|
||||
index,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
primaryKey,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
@@ -67,6 +69,7 @@ export const boards = pgTable(
|
||||
).enableRLS();
|
||||
|
||||
export const boardsRelations = relations(boards, ({ one, many }) => ({
|
||||
userFavorites: many(userBoardFavorites),
|
||||
createdBy: one(users, {
|
||||
fields: [boards.createdBy],
|
||||
references: [users.id],
|
||||
@@ -91,3 +94,21 @@ export const boardsRelations = relations(boards, ({ one, many }) => ({
|
||||
relationName: "boardWorkspace",
|
||||
}),
|
||||
}));
|
||||
|
||||
export const userBoardFavorites = pgTable(
|
||||
"user_board_favorites",
|
||||
{
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
boardId: bigint("boardId", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => boards.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
},
|
||||
(table) => ({
|
||||
pk: primaryKey({ columns: [table.userId, table.boardId] }),
|
||||
userIdx: index("user_board_favorite_user_idx").on(table.userId),
|
||||
boardIdx: index("user_board_favorite_board_idx").on(table.boardId),
|
||||
}),
|
||||
);
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
import { apikey } from "./auth";
|
||||
import { boards } from "./boards";
|
||||
import { boards, userBoardFavorites } from "./boards";
|
||||
import { cards } from "./cards";
|
||||
import { imports } from "./imports";
|
||||
import { lists } from "./lists";
|
||||
@@ -84,3 +84,14 @@ export const usersToWorkspacesRelations = relations(
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export const userBoardFavoritesRelations = relations(userBoardFavorites, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [userBoardFavorites.userId],
|
||||
references: [users.id],
|
||||
}),
|
||||
board: one(boards, {
|
||||
fields: [userBoardFavorites.boardId],
|
||||
references: [boards.id],
|
||||
}),
|
||||
}));
|
||||
Reference in New Issue
Block a user