Compare commits

...

3 Commits

Author SHA1 Message Date
Henry
f18021e560 fix: correct template creation condition in BoardDropdown 2026-06-30 11:59:00 +01:00
Henry
a994c061ac fix: prevent cross-tenant comment deletion and edit 2026-06-25 17:11:08 +01:00
Henry
6e1d821b35 fix: list dnd when board exceeds viewport width (#526) 2026-06-25 17:00:07 +01:00
4 changed files with 32 additions and 20 deletions

View File

@@ -88,7 +88,7 @@ export default function BoardDropdown({
const isArchiveActionPending = updateBoard.isPending; const isArchiveActionPending = updateBoard.isPending;
const items = [ const items = [
...(isTemplate && canCreateBoard ...(!isTemplate && canCreateBoard
? [ ? [
{ {
label: t`Make template`, label: t`Make template`,

View File

@@ -681,7 +681,7 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
> >
{(provided) => ( {(provided) => (
<div <div
className="flex" className="flex w-max"
ref={provided.innerRef} ref={provided.innerRef}
{...provided.droppableProps} {...provided.droppableProps}
> >

View File

@@ -8,20 +8,24 @@ import * as checklistRepo from "@kan/db/repository/checklist.repo";
import * as labelRepo from "@kan/db/repository/label.repo"; import * as labelRepo from "@kan/db/repository/label.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 { generateAttachmentUrl, generateAvatarUrl } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
import { import {
cardCreateResponseSchema,
cardUpdateResponseSchema,
cardDetailSchema,
commentResponseSchema,
commentDeleteResponseSchema,
activityItemSchema, activityItemSchema,
cardCreateResponseSchema,
cardDetailSchema,
cardUpdateResponseSchema,
commentDeleteResponseSchema,
commentResponseSchema,
} from "../schemas"; } from "../schemas";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
import { mergeActivities } from "../utils/activities"; import { mergeActivities } from "../utils/activities";
import { sendMentionEmails } from "../utils/notifications"; import { sendMentionEmails } from "../utils/notifications";
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions"; import {
import { generateAttachmentUrl, generateAvatarUrl } from "@kan/shared/utils"; assertCanDelete,
assertCanEdit,
assertPermission,
} from "../utils/permissions";
import { import {
createCardWebhookPayload, createCardWebhookPayload,
sendWebhooksForWorkspace, sendWebhooksForWorkspace,
@@ -246,7 +250,12 @@ export const cardRouter = createTRPCRouter({
code: "NOT_FOUND", code: "NOT_FOUND",
}); });
await assertPermission(ctx.db, userId, card.workspaceId, "comment:create"); await assertPermission(
ctx.db,
userId,
card.workspaceId,
"comment:create",
);
const newComment = await cardCommentRepo.create(ctx.db, { const newComment = await cardCommentRepo.create(ctx.db, {
comment: input.comment, comment: input.comment,
@@ -324,7 +333,7 @@ export const cardRouter = createTRPCRouter({
input.commentPublicId, input.commentPublicId,
); );
if (!existingComment) if (!existingComment || existingComment.cardId !== card.id)
throw new TRPCError({ throw new TRPCError({
message: `Comment with public ID ${input.commentPublicId} not found`, message: `Comment with public ID ${input.commentPublicId} not found`,
code: "NOT_FOUND", code: "NOT_FOUND",
@@ -412,7 +421,7 @@ export const cardRouter = createTRPCRouter({
input.commentPublicId, input.commentPublicId,
); );
if (!existingComment) if (!existingComment || existingComment.cardId !== card.id)
throw new TRPCError({ throw new TRPCError({
message: `Comment with public ID ${input.commentPublicId} not found`, message: `Comment with public ID ${input.commentPublicId} not found`,
code: "NOT_FOUND", code: "NOT_FOUND",
@@ -901,10 +910,7 @@ export const cardRouter = createTRPCRouter({
| undefined; | undefined;
if (input.listPublicId) { if (input.listPublicId) {
newList = await listRepo.getByPublicId( newList = await listRepo.getByPublicId(ctx.db, input.listPublicId);
ctx.db,
input.listPublicId,
);
if (!newList) if (!newList)
throw new TRPCError({ throw new TRPCError({
@@ -1048,12 +1054,14 @@ export const cardRouter = createTRPCRouter({
) { ) {
webhookChanges.dueDate = { from: previousDueDate, to: input.dueDate }; webhookChanges.dueDate = { from: previousDueDate, to: input.dueDate };
} }
const movedToNewList = Boolean(newListId && existingCard.listId !== newListId); const movedToNewList = Boolean(
newListId && existingCard.listId !== newListId,
);
const currentWebhookListPublicId = movedToNewList const currentWebhookListPublicId = movedToNewList
? input.listPublicId! ? input.listPublicId!
: existingCard.list.publicId; : existingCard.list.publicId;
const currentWebhookListName = movedToNewList const currentWebhookListName = movedToNewList
? newList?.name ?? card.listName ? (newList?.name ?? card.listName)
: existingCard.list.name; : existingCard.list.name;
if (movedToNewList) { if (movedToNewList) {
@@ -1291,7 +1299,10 @@ export const cardRouter = createTRPCRouter({
if (input.copyLabels && sourceCard.labels?.length) { if (input.copyLabels && sourceCard.labels?.length) {
const labelPublicIds = sourceCard.labels.map((l) => l.publicId); const labelPublicIds = sourceCard.labels.map((l) => l.publicId);
const labels = await labelRepo.getAllByPublicIds(ctx.db, labelPublicIds); const labels = await labelRepo.getAllByPublicIds(
ctx.db,
labelPublicIds,
);
if (labels.length) { if (labels.length) {
const labelsInsert = labels.map((label) => ({ const labelsInsert = labels.map((label) => ({
cardId: newCard.id, cardId: newCard.id,

View File

@@ -45,6 +45,7 @@ export const getByPublicId = (db: dbClient, publicId: string) => {
publicId: true, publicId: true,
comment: true, comment: true,
createdBy: true, createdBy: true,
cardId: true,
}, },
where: eq(comments.publicId, publicId), where: eq(comments.publicId, publicId),
}); });