Skip to content

feat: Archive/Trash for cards#420

Open
sai80082 wants to merge 3 commits into
kanbn:mainfrom
sai80082:feat/trash-archive-cards
Open

feat: Archive/Trash for cards#420
sai80082 wants to merge 3 commits into
kanbn:mainfrom
sai80082:feat/trash-archive-cards

Conversation

@sai80082
Copy link
Copy Markdown

@sai80082 sai80082 commented Feb 25, 2026

This pull request implements the archive card functionality, allowing users to move cards to an archived state instead of deleting them permanently.

This closes #307.

Below are the UI changes that are done for this feature.
image
image
image
image
image
image

@sai80082
Copy link
Copy Markdown
Author

Hey @hjball. Were you able to review this PR?

If there is some thing i need to change do let me know.

@hjball
Copy link
Copy Markdown
Contributor

hjball commented Apr 8, 2026

Sorry for the delay @sai80082 - I plan on reviewing this one in the next couple days

boardPublicId: z.string().min(12),
}),
)
.output(z.custom<Awaited<ReturnType<typeof cardRepo.getArchivedByBoardId>>>())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've deprecated this pattern now - all response types should be standard zod schemas (defined in the schemas dir)

const card = await cardRepo.getWorkspaceAndCardIdByCardPublicId(
ctx.db,
input.cardPublicId,
{ includeDeleted: true },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should guard against deletedAt being null - we only want to allow hard deleting card that have already been soft deleted, right?

card.createdBy,
);

await cardRepo.unarchive(ctx.db, {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we handle cases where the list has been archived?

};

const handleHardDelete = (cardPublicId: string) => {
if (confirm(t`Are you sure you want to permanently delete this card? This action cannot be undone.`)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the modal confirmation pattern instead of browser native dialog

]
: []),
{
label: t`Archived cards`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll only want to show these if the user has the correct permissions

"card:delete",
card.createdBy,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good if hard deleting created an activity log for the audit trail

export default function DeletedCardsView({ boardPublicId }: Props) {
const utils = api.useUtils();

const { data: deletedcards, isLoading } = api.board.deletedCards.useQuery(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const { data: deletedcards, isLoading } = api.board.deletedCards.useQuery(
const { data: deletedCards, isLoading } = api.board.deletedCards.useQuery(

// We need to join with lists to filter by board
return db
.select({
id: cards.id,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we exclude the internal ID from the response if it's not needed - the publicId should be sufficient

) => {
return db
.select({
id: cards.id,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this one

createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt"),
deletedAt: timestamp("deletedAt"),
archivedAt: timestamp("archivedAt"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add an index for archivedAt

Copy link
Copy Markdown
Contributor

@hjball hjball left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies again for the delay @sai80082

Great work so far - I've left some comments that will need to be addressed before this can be merged but I don't think it's far off

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] Concept of Done/Archive

2 participants