feat: create card activity from snapshot creation

This commit is contained in:
Henry
2025-10-10 13:45:01 +01:00
parent 8ba991a0d3
commit a41fe6ea3d
9 changed files with 2947 additions and 4 deletions

View File

@@ -215,13 +215,26 @@ export const boardRouter = createTRPCRouter({
// If sourceBoardPublicId is provided, clone the source board
if (input.sourceBoardPublicId) {
// First get the source board info (ID and type)
const sourceBoardInfo = await boardRepo.getIdByPublicId(
ctx.db,
input.sourceBoardPublicId,
);
if (!sourceBoardInfo)
throw new TRPCError({
message: `Source board with public ID ${input.sourceBoardPublicId} not found`,
code: "NOT_FOUND",
});
// Get the full board data with the correct type
const sourceBoard = await boardRepo.getByPublicId(
ctx.db,
input.sourceBoardPublicId,
{
members: [],
labels: [],
type: undefined,
type: sourceBoardInfo.type,
},
);
@@ -260,6 +273,7 @@ export const boardRouter = createTRPCRouter({
slug,
name: input.name,
type: input.type ?? "regular",
sourceBoardId: sourceBoardInfo.id,
});
if (!result)

View File

@@ -0,0 +1,6 @@
ALTER TABLE "card_activity" ADD COLUMN "sourceBoardId" bigint;--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "card_activity" ADD CONSTRAINT "card_activity_sourceBoardId_board_id_fk" FOREIGN KEY ("sourceBoardId") REFERENCES "public"."board"("id") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

File diff suppressed because it is too large Load Diff

View File

@@ -127,6 +127,13 @@
"when": 1759869689304,
"tag": "20251007204129_AddBoardTypeAndSourceIdColumns",
"breakpoints": true
},
{
"idx": 18,
"version": "7",
"when": 1760044396109,
"tag": "20251009211316_AddBoardSourceIdToCardActivity",
"breakpoints": true
}
]
}

View File

@@ -4,6 +4,7 @@ import type { dbClient } from "@kan/db/client";
import type { BoardVisibilityStatus } from "@kan/db/schema";
import {
boards,
cardActivities,
cards,
cardsToLabels,
cardToWorkspaceMembers,
@@ -54,6 +55,7 @@ export const getIdByPublicId = async (db: dbClient, boardPublicId: string) => {
const board = await db.query.boards.findFirst({
columns: {
id: true,
type: true,
},
where: eq(boards.publicId, boardPublicId),
});
@@ -707,6 +709,15 @@ export const createFromSnapshot = async (
if (!createdCard) throw new Error("Failed to create card");
// Create card.created activity
await tx.insert(cardActivities).values({
publicId: generateUID(),
type: "card.created",
cardId: createdCard.id,
createdBy: args.createdBy,
sourceBoardId: args.sourceBoardId,
});
if (card.labels.length) {
const cardLabels: { cardId: number; labelId: number }[] = [];
for (const label of card.labels) {
@@ -714,8 +725,20 @@ export const createFromSnapshot = async (
if (newLabelId)
cardLabels.push({ cardId: createdCard.id, labelId: newLabelId });
}
if (cardLabels.length)
if (cardLabels.length) {
await tx.insert(cardsToLabels).values(cardLabels);
// Create card.updated.label.added activities for each label
const labelActivities = cardLabels.map((cardLabel) => ({
publicId: generateUID(),
type: "card.updated.label.added" as const,
cardId: cardLabel.cardId,
labelId: cardLabel.labelId,
createdBy: args.createdBy,
sourceBoardId: args.sourceBoardId,
}));
await tx.insert(cardActivities).values(labelActivities);
}
}
if (card.checklists?.length) {
@@ -736,6 +759,16 @@ export const createFromSnapshot = async (
if (!createdChecklist) continue;
// Create card.updated.checklist.added activity
await tx.insert(cardActivities).values({
publicId: generateUID(),
type: "card.updated.checklist.added",
cardId: createdCard.id,
toTitle: checklist.name,
createdBy: args.createdBy,
sourceBoardId: args.sourceBoardId,
});
if (checklist.items.length) {
const itemValues = [...checklist.items]
.sort((a, b) => a.index - b.index)
@@ -748,8 +781,20 @@ export const createFromSnapshot = async (
completed: !!checklistItem.completed,
}));
if (itemValues.length)
if (itemValues.length) {
await tx.insert(checklistItems).values(itemValues);
// Create card.updated.checklist.item.added activities for each item
const itemActivities = itemValues.map((item) => ({
publicId: generateUID(),
type: "card.updated.checklist.item.added" as const,
cardId: createdCard.id,
toTitle: item.title,
createdBy: args.createdBy,
sourceBoardId: args.sourceBoardId,
}));
await tx.insert(cardActivities).values(itemActivities);
}
}
}
}

View File

@@ -22,6 +22,7 @@ export const create = async (
commentId?: number;
fromComment?: string;
toComment?: string;
sourceBoardId?: number;
},
) => {
const [result] = await db
@@ -44,6 +45,7 @@ export const create = async (
commentId: activityInput.commentId,
fromComment: activityInput.fromComment,
toComment: activityInput.toComment,
sourceBoardId: activityInput.sourceBoardId,
})
.returning({ id: cardActivities.id });
@@ -66,6 +68,7 @@ export const bulkCreate = async (
fromDescription?: string;
toDescription?: string;
createdBy: string;
sourceBoardId?: number;
}[],
) => {
const activitiesWithPublicIds = activityInputs.map((activity) => ({

View File

@@ -12,6 +12,7 @@ import {
varchar,
} from "drizzle-orm/pg-core";
import { boards } from "./boards";
import { checklists } from "./checklists";
import { imports } from "./imports";
import { labels } from "./labels";
@@ -133,6 +134,10 @@ export const cardActivities = pgTable("card_activity", {
),
fromComment: text("fromComment"),
toComment: text("toComment"),
sourceBoardId: bigint("sourceBoardId", { mode: "number" }).references(
() => boards.id,
{ onDelete: "set null" },
),
}).enableRLS();
export const cardActivitiesRelations = relations(cardActivities, ({ one }) => ({