Skip to content

Commit b886a6d

Browse files
authored
Merge pull request #910 from qunm00/feature/add-option-to-delete-all-cards-in-a-column
feat: allow user to delete all cards in a column
2 parents c69147b + ee2eace commit b886a6d

File tree

5 files changed

+84
-32
lines changed

5 files changed

+84
-32
lines changed

components/kanban/Column.vue

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. -->
3030
<div
3131
id="board-title"
3232
:class="[
33-
'flex flex-row items-start justify-between gap-4',
33+
'flex flex-row justify-between gap-4',
3434
titleTextClassZoom,
3535
]"
3636
>
@@ -66,36 +66,37 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. -->
6666
emitter.emit('columnActionDone');
6767
"
6868
/>
69-
70-
<div class="flex flex-row items-center gap-2">
71-
<Tooltip v-if="addToTopButtonShown" direction="top">
72-
<template #trigger>
73-
<PlusIcon
74-
:class="[
75-
'text-dim-4 text-accent-hover cursor-pointe mt-1.5 shrink-0 grow-0',
76-
iconSizeClass,
77-
]"
69+
70+
<Dropdown align="end">
71+
<template #trigger>
72+
<button
73+
class="bg-elevation-1 bg-elevation-2-hover transition-button h-full rounded-md"
74+
@click.prevent
75+
>
76+
<EllipsisHorizontalIcon class="size-6" />
77+
</button>
78+
</template>
79+
<template #content>
80+
<DropdownMenuItem
81+
class="bg-elevation-2-hover w-full cursor-pointer rounded-md px-4 py-1.5 pr-6 text-left flex items-center gap-2"
7882
@click="enableCardAddMode(true)"
79-
/>
80-
</template>
81-
82-
<template #content>{{
83-
$t("components.kanban.column.addCardTop")
84-
}}</template>
85-
</Tooltip>
86-
87-
<ClickCounter
88-
@double-click="$emit('removeColumnNoConfirmation', id)"
89-
@single-click="$emit('removeColumn', id)"
90-
>
91-
<XMarkIcon
92-
:class="[
93-
'text-dim-4 text-accent-hover mt-1.5 shrink-0 grow-0 cursor-pointer',
94-
iconSizeClass,
95-
]"
96-
/>
97-
</ClickCounter>
98-
</div>
83+
>
84+
{{$t('components.kanban.column.addCardTop')}}
85+
</DropdownMenuItem>
86+
<DropdownMenuItem
87+
class="bg-elevation-2-hover w-full cursor-pointer rounded-md px-4 py-1.5 pr-6 text-left flex items-center gap-2"
88+
@click="$emit('removeAllColumnCards', id)"
89+
>
90+
{{$t('components.kanban.card.deleteAllColumnCardsAction')}}
91+
</DropdownMenuItem>
92+
<DropdownMenuItem
93+
class="bg-elevation-2-hover w-full cursor-pointer rounded-md px-4 py-1.5 pr-6 text-left flex items-center gap-2"
94+
@click="$emit('removeColumn', id)"
95+
>
96+
{{$t('components.kanban.column.deleteColumnAction')}}
97+
</DropdownMenuItem>
98+
</template>
99+
</Dropdown>
99100
</div>
100101

101102
<Container
@@ -213,7 +214,7 @@ import type { Ref } from "vue";
213214
214215
import { applyDrag } from "@/utils/drag-n-drop";
215216
import emitter from "@/utils/emitter";
216-
import { PlusIcon, XMarkIcon } from "@heroicons/vue/24/solid";
217+
import { PlusIcon, EllipsisHorizontalIcon } from "@heroicons/vue/24/solid";
217218
//@ts-expect-error, sadly this library does not have ts typings
218219
import { Container, Draggable } from "vue3-smooth-dnd";
219220
import { useI18n } from "vue-i18n";
@@ -238,6 +239,7 @@ const emit = defineEmits<{
238239
(e: "openEditCardModal", columnId: string, el: Card): void;
239240
(e: "addCard", columnId: string, card: Card, addToTop: boolean | undefined): void;
240241
(e: "removeCard", columnId: string, cardId: string | undefined): void;
242+
(e: "removeAllColumnCards", columnId: string): void;
241243
(
242244
e: "removeCardWithConfirmation",
243245
columnId: string,

composables/useBoard.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ export function useBoard(id: string | Ref<string>) {
103103
if (!cardId) return;
104104
store.deleteCard(board.value.id, columnId, cardId);
105105
}
106+
107+
function deleteAllColumnCards(columnId: string) {
108+
if (!board.value) return;
109+
store.deleteAllColumnCards(board.value.id, columnId);
110+
}
106111

107112
function mutateCard(columnId: string, cardId: string, mut: Parameters<typeof store.mutateCard>[3]) {
108113
if (!board.value) return;
@@ -242,6 +247,7 @@ export function useBoard(id: string | Ref<string>) {
242247
// card actions
243248
createCard,
244249
deleteCard,
250+
deleteAllColumnCards,
245251
duplicateCard,
246252
mutateCard,
247253
reorderCards,

i18n/locales/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,10 @@
153153
},
154154
"card": {
155155
"deleteCardAction": "Delete card",
156-
"deleteCardConfirmation": "Are you sure you want to delete the card '{cardName}'? This action is irreversible."
156+
"deleteCardConfirmation": "Are you sure you want to delete the card '{cardName}'? This action is irreversible.",
157+
"deleteAllColumnCardsAction": "Delete all cards",
158+
"deleteAllColumnCardsConfirmation": "Are you sure you want to delete all the cards in column '{columnName}'? This action is irreversible.",
159+
"deleteAllColumnCardsConfirmationTitle": "Delete all cards in this column"
157160
},
158161
"searchBar": {
159162
"placeholder": "Search for cards..."

pages/kanban/[id].vue

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. -->
8787
@closeModal="cardRemoveDialog.cancel()"
8888
@confirmAction="cardRemoveDialog.confirm(true)"
8989
/>
90+
<ModalConfirmation
91+
v-show="removeAllColumnCardsModalVisible"
92+
:close-button-text="$t('general.cancelAction')"
93+
:confirm-button-text="$t('general.deleteAction')"
94+
:title="$t('components.kanban.card.deleteAllColumnCardsConfirmationTitle') + '?'"
95+
@closeModal="allColumnCardsRemoveDialog.cancel()"
96+
@confirmAction="allColumnCardsRemoveDialog.confirm(true)"
97+
/>
9098

9199
<div class="absolute top-4 z-50 ml-8 w-[calc(100vw-112px)]">
92100
<h1
@@ -261,6 +269,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. -->
261269
@addCard="board.createCard"
262270
@removeCard="board.deleteCard"
263271
@removeCardWithConfirmation="removeCardWithConfirmation"
272+
@removeAllColumnCards="removeAllColumnCards"
264273
@removeColumn="openColumnRemoveDialog(column.id)"
265274
@removeColumnNoConfirmation="board.removeColumn"
266275
@setColumnEditIndex="setColumnEditIndex"
@@ -360,13 +369,15 @@ const currentlyActiveCardInfo: {
360369
361370
const removeColumnModalVisible = ref(false);
362371
const removeCardModalVisible = ref(false);
372+
const removeAllColumnCardsModalVisible = ref(false);
363373
const deleteBoardModalVisible = ref(false);
364374
const renameBoardModalVisible = ref(false);
365375
366376
const editTagModalVisible = ref(false);
367377
368378
const columnRemoveDialog = useConfirmDialog(removeColumnModalVisible);
369379
const cardRemoveDialog = useConfirmDialog(removeCardModalVisible);
380+
const allColumnCardsRemoveDialog = useConfirmDialog(removeAllColumnCardsModalVisible);
370381
371382
const board = useBoard(computed(() => route.params.id as string));
372383
const { board: boardContent } = board;
@@ -665,6 +676,27 @@ const removeCardWithConfirmation = async (
665676
emitter.emit("columnDraggingOn");
666677
};
667678
679+
const removeAllColumnCards = async (
680+
columnID: string
681+
) => {
682+
const column = boardContent.value?.columns.filter((obj: Column) => {
683+
return obj.id === columnID;
684+
})[0];
685+
686+
emitter.emit("openModalWithCustomDescription", {
687+
description: t("components.kanban.card.deleteAllColumnCardsConfirmation", {
688+
columnName: column.title,
689+
}),
690+
});
691+
692+
const { isCanceled } = await allColumnCardsRemoveDialog.reveal();
693+
if (!isCanceled) {
694+
setTimeout(() => {
695+
board.deleteAllColumnCards(columnID);
696+
}, 250);
697+
}
698+
}
699+
668700
/**
669701
* Get the text color with the correct contrast if a background image is set
670702
*/

stores/boards.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,15 @@ export const useBoardsStore = defineStore("boards", {
311311
col.cards = col.cards.filter(c => c.id !== cardId);
312312
b.lastEdited = new Date();
313313
},
314+
deleteAllColumnCards(boardId: string, columnId: string) {
315+
const b = this.boardById(boardId);
316+
if (!b) return;
317+
const col = b.columns.find(c => c.id === columnId);
318+
if (!col) return;
319+
320+
col.cards = [];
321+
b.lastEdited = new Date();
322+
},
314323
mutateCard(boardId: string, columnId: string, cardId: string, mut: (c: Card) => void) {
315324
const b = this.boardById(boardId);
316325
if (!b) return;

0 commit comments

Comments
 (0)