feat: set up command pallette and search endpoint
This commit is contained in:
@@ -409,4 +409,74 @@ export const workspaceRouter = createTRPCRouter({
|
||||
isReserved: workspaceSlug?.type === "reserved",
|
||||
};
|
||||
}),
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search boards and cards in a workspace",
|
||||
method: "GET",
|
||||
path: "/workspaces/{workspacePublicId}/search",
|
||||
description:
|
||||
"Searches for boards and cards by title within a workspace",
|
||||
tags: ["Workspaces"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
workspacePublicId: z.string().min(12),
|
||||
query: z.string().min(1).max(100),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z.array(
|
||||
z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
publicId: z.string(),
|
||||
title: z.string(),
|
||||
description: z.string().nullable(),
|
||||
slug: z.string(),
|
||||
type: z.literal("board"),
|
||||
}),
|
||||
z.object({
|
||||
publicId: z.string(),
|
||||
title: z.string(),
|
||||
description: z.string().nullable(),
|
||||
boardPublicId: z.string(),
|
||||
boardName: z.string(),
|
||||
listName: z.string(),
|
||||
type: z.literal("card"),
|
||||
}),
|
||||
]),
|
||||
),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const userId = ctx.user?.id;
|
||||
|
||||
if (!userId)
|
||||
throw new TRPCError({
|
||||
message: `User not authenticated`,
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
|
||||
const workspace = await workspaceRepo.getByPublicId(
|
||||
ctx.db,
|
||||
input.workspacePublicId,
|
||||
);
|
||||
|
||||
if (!workspace)
|
||||
throw new TRPCError({
|
||||
message: `Workspace not found`,
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertUserInWorkspace(ctx.db, userId, workspace.id);
|
||||
|
||||
const result = await workspaceRepo.searchBoardsAndCards(
|
||||
ctx.db,
|
||||
workspace.id,
|
||||
input.query,
|
||||
);
|
||||
|
||||
return result;
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { and, eq, inArray, isNull } from "drizzle-orm";
|
||||
import { and, eq, ilike, inArray, isNull } from "drizzle-orm";
|
||||
|
||||
import type { dbClient } from "@kan/db/client";
|
||||
import { boards, workspaceMembers, workspaces } from "@kan/db/schema";
|
||||
import {
|
||||
boards,
|
||||
cards,
|
||||
lists,
|
||||
workspaceMembers,
|
||||
workspaces,
|
||||
} from "@kan/db/schema";
|
||||
import { generateUID } from "@kan/shared/utils";
|
||||
|
||||
export const create = async (
|
||||
@@ -274,3 +280,59 @@ export const isUserInWorkspace = async (
|
||||
|
||||
return result?.id !== undefined;
|
||||
};
|
||||
|
||||
export const searchBoardsAndCards = async (
|
||||
db: dbClient,
|
||||
workspaceId: number,
|
||||
query: string,
|
||||
) => {
|
||||
const searchQuery = `%${query}%`;
|
||||
|
||||
// Search for boards
|
||||
const boardResults = await db
|
||||
.select({
|
||||
publicId: boards.publicId,
|
||||
title: boards.name,
|
||||
description: boards.description,
|
||||
slug: boards.slug,
|
||||
})
|
||||
.from(boards)
|
||||
.where(
|
||||
and(
|
||||
eq(boards.workspaceId, workspaceId),
|
||||
ilike(boards.name, searchQuery),
|
||||
isNull(boards.deletedAt),
|
||||
),
|
||||
);
|
||||
|
||||
// Search for cards
|
||||
const cardResults = await db
|
||||
.select({
|
||||
publicId: cards.publicId,
|
||||
title: cards.title,
|
||||
description: cards.description,
|
||||
boardPublicId: boards.publicId,
|
||||
boardName: boards.name,
|
||||
listName: lists.name,
|
||||
})
|
||||
.from(cards)
|
||||
.innerJoin(lists, eq(cards.listId, lists.id))
|
||||
.innerJoin(boards, eq(lists.boardId, boards.id))
|
||||
.where(
|
||||
and(
|
||||
eq(boards.workspaceId, workspaceId),
|
||||
ilike(cards.title, searchQuery),
|
||||
isNull(cards.deletedAt),
|
||||
isNull(lists.deletedAt),
|
||||
isNull(boards.deletedAt),
|
||||
),
|
||||
);
|
||||
|
||||
// Combine results
|
||||
const allResults = [
|
||||
...boardResults.map((board) => ({ ...board, type: "board" as const })),
|
||||
...cardResults.map((card) => ({ ...card, type: "card" as const })),
|
||||
];
|
||||
|
||||
return allResults;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user