fix: generate board slug for new boards
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { HiXMark } from "react-icons/hi2";
|
import { HiXMark } from "react-icons/hi2";
|
||||||
|
|
||||||
import { type NewBoardInput } from "@kan/api/types";
|
import type { NewBoardInput } from "@kan/api/types";
|
||||||
|
|
||||||
|
import Button from "~/components/Button";
|
||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { useWorkspace } from "~/providers/workspace";
|
import { useWorkspace } from "~/providers/workspace";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
@@ -15,7 +16,7 @@ export function NewBoardForm() {
|
|||||||
const { register, handleSubmit } = useForm<NewBoardInput>({
|
const { register, handleSubmit } = useForm<NewBoardInput>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
workspacePublicId: workspace?.publicId || "",
|
workspacePublicId: workspace.publicId || "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,12 +58,9 @@ export function NewBoardForm() {
|
|||||||
|
|
||||||
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||||
<div>
|
<div>
|
||||||
<button
|
<Button type="submit" isLoading={createBoard.isPending}>
|
||||||
type="submit"
|
|
||||||
className="inline-flex w-full justify-center rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 shadow-sm focus-visible:outline-none dark:bg-dark-1000 dark:text-dark-50"
|
|
||||||
>
|
|
||||||
Create board
|
Create board
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import * as cardRepo from "@kan/db/repository/card.repo";
|
|||||||
import * as activityRepo from "@kan/db/repository/cardActivity.repo";
|
import * as activityRepo from "@kan/db/repository/cardActivity.repo";
|
||||||
import * as listRepo from "@kan/db/repository/list.repo";
|
import * as listRepo from "@kan/db/repository/list.repo";
|
||||||
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||||
|
import { generateSlug, generateUID } from "@kan/utils";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||||
|
|
||||||
@@ -141,7 +142,18 @@ export const boardRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let slug = generateSlug(input.name);
|
||||||
|
|
||||||
|
const isSlugUnique = await boardRepo.isSlugUnique(ctx.db, {
|
||||||
|
slug,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isSlugUnique) slug = `${slug}-${generateUID()}`;
|
||||||
|
|
||||||
const result = await boardRepo.create(ctx.db, {
|
const result = await boardRepo.create(ctx.db, {
|
||||||
|
publicId: generateUID(),
|
||||||
|
slug,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
|
|||||||
@@ -210,20 +210,23 @@ export const getWithLatestListIndexByPublicId = async (
|
|||||||
export const create = async (
|
export const create = async (
|
||||||
db: SupabaseClient<Database>,
|
db: SupabaseClient<Database>,
|
||||||
boardInput: {
|
boardInput: {
|
||||||
|
publicId?: string;
|
||||||
name: string;
|
name: string;
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
workspaceId: number;
|
workspaceId: number;
|
||||||
importId?: number;
|
importId?: number;
|
||||||
|
slug: string;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const { data } = await db
|
const { data } = await db
|
||||||
.from("board")
|
.from("board")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: boardInput.publicId ?? generateUID(),
|
||||||
name: boardInput.name,
|
name: boardInput.name,
|
||||||
createdBy: boardInput.createdBy,
|
createdBy: boardInput.createdBy,
|
||||||
workspaceId: boardInput.workspaceId,
|
workspaceId: boardInput.workspaceId,
|
||||||
importId: boardInput.importId,
|
importId: boardInput.importId,
|
||||||
|
slug: boardInput.slug,
|
||||||
})
|
})
|
||||||
.select(`id, publicId, name`)
|
.select(`id, publicId, name`)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
@@ -277,3 +280,19 @@ export const hardDelete = async (
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isSlugUnique = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: { slug: string; workspaceId: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.select("slug")
|
||||||
|
.eq("slug", args.slug)
|
||||||
|
.eq("workspaceId", args.workspaceId)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return !data;
|
||||||
|
};
|
||||||
|
|||||||
8
packages/utils/src/generateSlug.ts
Normal file
8
packages/utils/src/generateSlug.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export function generateSlug(text: string): string {
|
||||||
|
return text
|
||||||
|
.toLowerCase()
|
||||||
|
.trim()
|
||||||
|
.replace(/[^\w\s-]/g, "") // Remove special characters
|
||||||
|
.replace(/\s+/g, "-") // Replace spaces with hyphens
|
||||||
|
.replace(/-+/g, "-"); // Remove consecutive hyphens
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export const name = "utils";
|
export const name = "utils";
|
||||||
|
|
||||||
export { generateUID } from "./generateUID";
|
export { generateUID } from "./generateUID";
|
||||||
|
export { generateSlug } from "./generateSlug";
|
||||||
|
|||||||
Reference in New Issue
Block a user