-
Notifications
You must be signed in to change notification settings - Fork 47
fix: added the missing delete collection on the collections details page #1790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
5b17ae7
0aa53a2
abc2ee6
6a2b320
408bc06
58b3896
2f28a37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -145,6 +145,29 @@ SPDX-License-Identifier: MIT | |||||
| <lfx-icon name="share-nodes" /> | ||||||
| Share | ||||||
| </lfx-button> | ||||||
|
|
||||||
| <lfx-dropdown | ||||||
| v-if="props.type === 'my-collections'" | ||||||
| placement="bottom-end" | ||||||
| :class="isDeleting ? 'opacity-50 cursor-not-allowed' : ''" | ||||||
| :disabled="isDeleting" | ||||||
| > | ||||||
| <template #trigger> | ||||||
| <lfx-icon-button | ||||||
| icon="ellipsis" | ||||||
| type="transparent" | ||||||
| class="!text-neutral-900" | ||||||
| /> | ||||||
| </template> | ||||||
| <lfx-dropdown-item @click.stop.prevent="handleDelete"> | ||||||
| <lfx-icon | ||||||
| name="trash" | ||||||
| :size="16" | ||||||
| class="!text-negative-500" | ||||||
| /> | ||||||
| <span class="text-negative-500">Delete</span> | ||||||
| </lfx-dropdown-item> | ||||||
| </lfx-dropdown> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
@@ -216,18 +239,34 @@ SPDX-License-Identifier: MIT | |||||
| </div> | ||||||
| </section> | ||||||
| </div> | ||||||
|
|
||||||
| <!-- Full-page loading overlay for delete operation --> | ||||||
| <teleport to="body"> | ||||||
| <div | ||||||
| v-if="isDeleting" | ||||||
| class="fixed inset-0 z-50 flex flex-col items-center justify-center bg-black/50" | ||||||
| > | ||||||
| <lfx-spinner | ||||||
| :size="48" | ||||||
| class="text-white" | ||||||
| /> | ||||||
| <p class="mt-4 text-white text-sm font-medium">Deleting collection...</p> | ||||||
| </div> | ||||||
| </teleport> | ||||||
| </template> | ||||||
|
|
||||||
| <script lang="ts" setup> | ||||||
| import { computed } from 'vue'; | ||||||
| import { computed, ref } from 'vue'; | ||||||
| import { storeToRefs } from 'pinia'; | ||||||
| import { useRouter } from 'nuxt/app'; | ||||||
| import { useQueryClient } from '@tanstack/vue-query'; | ||||||
| import { collectionTabs, headerBackground } from '../../config/collection-type-config'; | ||||||
| import type { Collection } from '~~/types/collection'; | ||||||
| import LfxIconButton from '~/components/uikit/icon-button/icon-button.vue'; | ||||||
| import LfxIcon from '~/components/uikit/icon/icon.vue'; | ||||||
| import useScroll from '~/components/shared/utils/scroll'; | ||||||
| import LfxSkeleton from '~/components/uikit/skeleton/skeleton.vue'; | ||||||
| import LfxSpinner from '~/components/uikit/spinner/spinner.vue'; | ||||||
| import LfxButton from '~/components/uikit/button/button.vue'; | ||||||
| import CollectionOwner from '~/components/shared/components/collection-owner.vue'; | ||||||
| import { formatDate } from '~/components/shared/utils/formatter'; | ||||||
|
|
@@ -240,9 +279,16 @@ import LfxLikeButton from '~/components/shared/components/like-button.vue'; | |||||
| import LfxTooltip from '~/components/uikit/tooltip/tooltip.vue'; | ||||||
| import { useEditCollectionStore } from '~/components/modules/collection/store/edit-collection.store'; | ||||||
| import { useDuplicateCollectionStore } from '~/components/modules/collection/store/duplicate-collection.store'; | ||||||
| import LfxDropdown from '~/components/uikit/dropdown/dropdown.vue'; | ||||||
| import LfxDropdownItem from '~/components/uikit/dropdown/dropdown-item.vue'; | ||||||
| import { useConfirmStore } from '~/components/shared/modules/confirm/store/confirm.store'; | ||||||
| import { TanstackKey } from '~/components/shared/types/tanstack'; | ||||||
| import { COLLECTIONS_API_SERVICE } from '~/components/modules/collection/services/collections.api.service'; | ||||||
|
|
||||||
| const { openEditModal } = useEditCollectionStore(); | ||||||
| const { openDuplicateModal } = useDuplicateCollectionStore(); | ||||||
| const { openConfirmModal } = useConfirmStore(); | ||||||
| const queryClient = useQueryClient(); | ||||||
|
|
||||||
| const authStore = useAuthStore(); | ||||||
| const { user } = storeToRefs(authStore); | ||||||
|
|
@@ -287,6 +333,8 @@ const currentSort = computed(() => { | |||||
| return { field: props.sort, direction: 'asc' as const }; | ||||||
| }); | ||||||
|
|
||||||
| const isDeleting = ref(false); | ||||||
|
|
||||||
| const handleSort = (field: string) => { | ||||||
| const defaultDirections: Record<string, 'asc' | 'desc'> = { | ||||||
| name: 'asc', | ||||||
|
|
@@ -347,6 +395,32 @@ const handleClone = () => { | |||||
| }); | ||||||
| } | ||||||
| }; | ||||||
|
|
||||||
| const handleDelete = () => { | ||||||
| if (props.collection && !isDeleting.value) { | ||||||
| openConfirmModal({ | ||||||
| title: 'Delete collection', | ||||||
| message: `Are you sure you want to delete this collection?`, | ||||||
| confirmLabel: 'Delete', | ||||||
| cancelLabel: 'Cancel', | ||||||
| }).then(async (result) => { | ||||||
| if (result) { | ||||||
| isDeleting.value = true; | ||||||
| await COLLECTIONS_API_SERVICE.deleteCollection(props.collection!.id); | ||||||
|
|
||||||
| invalidateMyCollections(); | ||||||
| isDeleting.value = false; | ||||||
| router.push({ name: LfxRoutes.COLLECTIONS_MY_COLLECTIONS }); | ||||||
emlimlf marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| } | ||||||
| }); | ||||||
| } | ||||||
| }; | ||||||
|
|
||||||
| const invalidateMyCollections = () => { | ||||||
| queryClient.invalidateQueries({ | ||||||
| queryKey: [TanstackKey.MY_COLLECTIONS, 'starred_desc', undefined, 99, undefined, user.value?.sub], | ||||||
|
||||||
| queryKey: [TanstackKey.MY_COLLECTIONS, 'starred_desc', undefined, 99, undefined, user.value?.sub], | |
| queryKey: [TanstackKey.MY_COLLECTIONS], |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <!-- | ||
| Copyright (c) 2025 The Linux Foundation and each contributor. | ||
| SPDX-License-Identifier: MIT | ||
| --> | ||
| <template> | ||
| <lfx-confirm-modal | ||
| v-if="isConfirmModalOpen" | ||
| v-model="isConfirmModalOpen" | ||
| :options="confirmOptions" | ||
| @confirm="confirm" | ||
| @cancel="cancel" | ||
| /> | ||
| </template> | ||
|
|
||
| <script lang="ts" setup> | ||
| import { storeToRefs } from 'pinia'; | ||
| import LfxConfirmModal from '~/components/shared/modules/confirm/components/confirm-modal.vue'; | ||
| import { useConfirmStore } from '~/components/shared/modules/confirm/store/confirm.store'; | ||
|
|
||
| const confirmStore = useConfirmStore(); | ||
| const { isConfirmModalOpen, confirmOptions } = storeToRefs(confirmStore); | ||
| const { confirm, cancel } = confirmStore; | ||
| </script> | ||
|
|
||
| <script lang="ts"> | ||
| export default { | ||
| name: 'LfxConfirmGlobal', | ||
| }; | ||
| </script> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| <!-- | ||
| Copyright (c) 2025 The Linux Foundation and each contributor. | ||
| SPDX-License-Identifier: MIT | ||
| --> | ||
| <template> | ||
| <lfx-modal | ||
| v-model="isModalOpen" | ||
| width="25rem" | ||
| > | ||
| <section class="p-6"> | ||
| <div class="flex justify-between items-start"> | ||
| <h3 class="text-heading-3 font-bold text-neutral-900"> | ||
| {{ options.title }} | ||
| </h3> | ||
| <lfx-icon-button | ||
| icon="close" | ||
| size="small" | ||
| @click="handleCancel" | ||
| /> | ||
| </div> | ||
|
|
||
| <p class="mt-4 text-body-2 text-neutral-700"> | ||
| {{ options.message }} | ||
| </p> | ||
|
|
||
| <div class="mt-6 flex justify-end gap-3"> | ||
| <lfx-button | ||
| type="primary" | ||
| size="small" | ||
| :label="options.confirmLabel" | ||
| @click="handleConfirm" | ||
| /> | ||
| <lfx-button | ||
| type="transparent" | ||
| size="small" | ||
| :label="options.cancelLabel" | ||
| @click="handleCancel" | ||
| /> | ||
| </div> | ||
| </section> | ||
| </lfx-modal> | ||
| </template> | ||
|
|
||
| <script lang="ts" setup> | ||
| import { computed } from 'vue'; | ||
| import LfxModal from '~/components/uikit/modal/modal.vue'; | ||
| import LfxIconButton from '~/components/uikit/icon-button/icon-button.vue'; | ||
| import LfxButton from '~/components/uikit/button/button.vue'; | ||
| import type { ConfirmOptions } from '~/components/shared/modules/confirm/store/confirm.store'; | ||
|
|
||
| const props = defineProps<{ | ||
| modelValue: boolean; | ||
| options: ConfirmOptions; | ||
| }>(); | ||
|
|
||
| const emit = defineEmits<{ | ||
| (e: 'update:modelValue', value: boolean): void; | ||
| (e: 'confirm'): void; | ||
| (e: 'cancel'): void; | ||
| }>(); | ||
|
|
||
| const isModalOpen = computed({ | ||
| get: () => props.modelValue, | ||
| set: (value: boolean) => { | ||
| emit('update:modelValue', value); | ||
| if (!value) { | ||
| emit('cancel'); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| const handleConfirm = () => { | ||
| emit('confirm'); | ||
| }; | ||
|
|
||
| const handleCancel = () => { | ||
| emit('cancel'); | ||
| }; | ||
| </script> | ||
|
|
||
| <script lang="ts"> | ||
| export default { | ||
| name: 'LfxConfirmModal', | ||
| }; | ||
| </script> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| // Copyright (c) 2025 The Linux Foundation and each contributor. | ||
| // SPDX-License-Identifier: MIT | ||
| import { defineStore } from 'pinia'; | ||
| import { ref } from 'vue'; | ||
|
|
||
| export interface ConfirmOptions { | ||
| title?: string; | ||
| message: string; | ||
| confirmLabel?: string; | ||
| cancelLabel?: string; | ||
| } | ||
|
|
||
| const defaultOptions: ConfirmOptions = { | ||
| title: 'Confirm', | ||
| message: '', | ||
| confirmLabel: 'Ok', | ||
| cancelLabel: 'Cancel', | ||
| }; | ||
|
|
||
| export const useConfirmStore = defineStore('confirm', () => { | ||
| const isConfirmModalOpen = ref(false); | ||
| const confirmOptions = ref<ConfirmOptions>(defaultOptions); | ||
| const resolvePromise = ref<((value: boolean) => void) | null>(null); | ||
|
|
||
| const openConfirmModal = (options: ConfirmOptions): Promise<boolean> => { | ||
| confirmOptions.value = { ...defaultOptions, ...options }; | ||
| isConfirmModalOpen.value = true; | ||
|
|
||
| return new Promise((resolve) => { | ||
| resolvePromise.value = resolve; | ||
| }); | ||
| }; | ||
|
|
||
| const confirm = () => { | ||
| isConfirmModalOpen.value = false; | ||
| resolvePromise.value?.(true); | ||
| resolvePromise.value = null; | ||
| }; | ||
|
|
||
| const cancel = () => { | ||
| isConfirmModalOpen.value = false; | ||
| resolvePromise.value?.(false); | ||
| resolvePromise.value = null; | ||
emlimlf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| return { | ||
| isConfirmModalOpen, | ||
| confirmOptions, | ||
| openConfirmModal, | ||
| confirm, | ||
| cancel, | ||
| }; | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.