Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ SPDX-License-Identifier: MIT
/>
</lfx-tooltip>
<lfx-button
v-if="props.type === 'my-collections'"
v-if="props.type === CollectionTypeEnum.MY_COLLECTIONS"
type="outline"
class="!rounded-full"
@click="handleEdit"
Expand All @@ -145,6 +145,29 @@ SPDX-License-Identifier: MIT
<lfx-icon name="share-nodes" />
Share
</lfx-button>

<lfx-dropdown
v-if="props.type === CollectionTypeEnum.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>
Expand Down Expand Up @@ -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 { collectionTabs, headerBackground } from '../../config/collection-type-config';
import { useQueryClient } from '@tanstack/vue-query';
import { collectionTabs, headerBackground, CollectionTypeEnum } 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';
Expand All @@ -240,9 +279,19 @@ 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';
import { ToastTypesEnum } from '~/components/uikit/toast/types/toast.types';
import useToastService from '~/components/uikit/toast/toast.service';

const { openEditModal } = useEditCollectionStore();
const { openDuplicateModal } = useDuplicateCollectionStore();
const { openConfirmModal } = useConfirmStore();
const { showToast } = useToastService();
const queryClient = useQueryClient();

const authStore = useAuthStore();
const { user } = storeToRefs(authStore);
Expand Down Expand Up @@ -287,6 +336,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',
Expand Down Expand Up @@ -347,6 +398,39 @@ const handleClone = () => {
});
}
};

const handleDelete = () => {
if (props.collection && !isDeleting.value) {
try {
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 });
}
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to delete collection';
showToast(message, ToastTypesEnum.negative);
} finally {
isDeleting.value = false;
}
}
};

const invalidateMyCollections = () => {
queryClient.invalidateQueries({
queryKey: [TanstackKey.MY_COLLECTIONS],
});
};
</script>

<script lang="ts">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import type { MenuItem } from '~/config/menu';
import type { CollectionType } from '~~/types/collection';
import type { User } from '~~/types/auth/auth-user.types';

export enum CollectionTypeEnum {
CURATED = 'curated',
COMMUNITY = 'community',
MY_COLLECTIONS = 'my-collections',
}

export interface CollectionTypesTabs extends MenuItem {
type: CollectionType;
description: string;
Expand All @@ -20,7 +26,7 @@ export const collectionTabs = (user: User | null): CollectionTypesTabs[] => {
route: LfxRoutes.COLLECTIONS_CURATED,
activeClass: '!bg-neutral-200',
iconHighlightClass: '!bg-neutral-900',
type: 'curated',
type: CollectionTypeEnum.CURATED,
description: 'Hand-picked collections from The Linux Foundation.',
},
];
Expand All @@ -32,7 +38,7 @@ export const collectionTabs = (user: User | null): CollectionTypesTabs[] => {
route: LfxRoutes.COLLECTIONS_COMMUNITY,
activeClass: '!bg-accent-200',
iconHighlightClass: '!bg-accent-500',
type: 'community',
type: CollectionTypeEnum.COMMUNITY,
description: 'Discover collections from the open source community.',
});

Expand All @@ -44,7 +50,7 @@ export const collectionTabs = (user: User | null): CollectionTypesTabs[] => {
route: LfxRoutes.COLLECTIONS_MY_COLLECTIONS,
activeClass: '!bg-discovery-200',
iconHighlightClass: '!bg-discovery-500',
type: 'my-collections',
type: CollectionTypeEnum.MY_COLLECTIONS,
description: "Collections you've created or liked.",
});
}
Expand All @@ -55,13 +61,13 @@ export const collectionTabs = (user: User | null): CollectionTypesTabs[] => {
// This only applies to the collection details page header
export const headerBackground = (type?: CollectionType, curatedColor?: string | null) => {
switch (type) {
case 'curated':
case CollectionTypeEnum.CURATED:
return {
background: curatedColor
? `linear-gradient(0deg, ${curatedColor}00, ${curatedColor}0D), var(--White, #FFF)`
: 'linear-gradient(0deg, #0F172B00, #0F172B0D), var(--White, #FFF)',
};
case 'community':
case CollectionTypeEnum.COMMUNITY:
return {
background: 'linear-gradient(0deg, #009AFF00, #009AFF0D), var(--White, #FFF)',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { ProjectInsights } from '~~/types/project';
import { TanstackKey } from '~/components/shared/types/tanstack';
import type { SearchProject, SearchResults } from '~~/types/search';
import { type User } from '~~/types/auth/auth-user.types';
import { CollectionTypeEnum } from '~/components/modules/collection/config/collection-type-config';

export interface CategoryGroupOptions {
value: string;
Expand Down Expand Up @@ -220,10 +221,10 @@ class CollectionsApiService {
}

fetchDiscoveryCuratedCollections() {
const params = this.discoveryParams('curated');
const params = this.discoveryParams(CollectionTypeEnum.CURATED);
const queryKey = computed(() => [
TanstackKey.COLLECTION_DISCOVERY,
'curated',
CollectionTypeEnum.CURATED,
params.sort,
params.pageSize,
params.type,
Expand All @@ -238,10 +239,10 @@ class CollectionsApiService {
}

fetchDiscoveryCommunityCollections() {
const params = this.discoveryParams('community');
const params = this.discoveryParams(CollectionTypeEnum.COMMUNITY);
const queryKey = computed(() => [
TanstackKey.COLLECTION_DISCOVERY,
'community',
CollectionTypeEnum.COMMUNITY,
params.sort,
params.pageSize,
params.type,
Expand All @@ -256,10 +257,10 @@ class CollectionsApiService {
}

fetchDiscoveryMyCollections(user: User | null) {
const params = this.discoveryParams('my-collections');
const params = this.discoveryParams(CollectionTypeEnum.MY_COLLECTIONS);
const queryKey = computed(() => [
TanstackKey.COLLECTION_DISCOVERY,
'my',
CollectionTypeEnum.MY_COLLECTIONS,
params.sort,
params.pageSize,
params.type,
Expand All @@ -277,14 +278,21 @@ class CollectionsApiService {
async prefetchDiscoveryCollections() {
const queryClient = useQueryClient();

const curatedQueryKey = [TanstackKey.COLLECTION_DISCOVERY, 'curated'];
const communityQueryKey = [TanstackKey.COLLECTION_DISCOVERY, 'community'];
const myCollectionsQueryKey = [TanstackKey.COLLECTION_DISCOVERY, 'my'];
const curatedQueryKey = [TanstackKey.COLLECTION_DISCOVERY, CollectionTypeEnum.CURATED];
const communityQueryKey = [TanstackKey.COLLECTION_DISCOVERY, CollectionTypeEnum.COMMUNITY];
const myCollectionsQueryKey = [
TanstackKey.COLLECTION_DISCOVERY,
CollectionTypeEnum.MY_COLLECTIONS,
];

const curatedQueryFn = this.fetchCollectionsQueryFn(() => this.discoveryParams('curated'));
const communityQueryFn = this.fetchCollectionsQueryFn(() => this.discoveryParams('community'));
const curatedQueryFn = this.fetchCollectionsQueryFn(() =>
this.discoveryParams(CollectionTypeEnum.CURATED),
);
const communityQueryFn = this.fetchCollectionsQueryFn(() =>
this.discoveryParams(CollectionTypeEnum.COMMUNITY),
);
const myCollectionsQueryFn = this.fetchMyCollectionsQueryFn(() =>
this.discoveryParams('my-collections'),
this.discoveryParams(CollectionTypeEnum.MY_COLLECTIONS),
);

await Promise.all([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ import type { Project } from '~~/types/project';
import { useAuthStore } from '~/components/modules/auth/store/auth.store';
import { TanstackKey } from '~/components/shared/types/tanstack';
import { useLikeCounts } from '~/components/modules/collection/composables/useLikeCounts';
import { CollectionTypeEnum } from '~/components/modules/collection/config/collection-type-config';

const props = defineProps<{
slug: string;
Expand Down Expand Up @@ -152,10 +153,10 @@ const { queryParams } = useQueryParam(collectionDetailsParamsGetter, collectionL
const { onlyLFProjects, collectionSort } = queryParams.value;
const collectionType = computed<CollectionType>(() => {
if (user.value && user.value.sub === currentCollection.value?.ssoUserId) {
return 'my-collections';
return CollectionTypeEnum.MY_COLLECTIONS;
}

return currentCollection.value?.ssoUserId ? 'community' : 'curated';
return currentCollection.value?.ssoUserId ? CollectionTypeEnum.COMMUNITY : CollectionTypeEnum.CURATED;
});

const sort = ref(collectionSort || 'contributorCount_desc');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ SPDX-License-Identifier: MIT
v-for="collection in flatData"
:key="collection.slug"
:collection="collection"
:show-like-count="props.type !== 'my-collections'"
:show-like-count="props.type !== CollectionTypeEnum.MY_COLLECTIONS"
:variant="props.type"
@updated="refreshList"
/>
Expand Down Expand Up @@ -91,7 +91,7 @@ SPDX-License-Identifier: MIT
</div>

<section
v-if="props.type === 'my-collections'"
v-if="props.type === CollectionTypeEnum.MY_COLLECTIONS"
class="container mt-10"
>
<lfx-liked-collections :view="view" />
Expand Down Expand Up @@ -124,6 +124,7 @@ import { useBannerStore } from '~/components/shared/store/banner.store';
import { useAuthStore } from '~/components/modules/auth/store/auth.store';
import { useCollectionsStore } from '~/components/modules/collection/store/collections.store';
import { useLikeCounts } from '~/components/modules/collection/composables/useLikeCounts';
import { CollectionTypeEnum } from '~/components/modules/collection/config/collection-type-config';

const props = defineProps<{
type?: CollectionType;
Expand All @@ -146,11 +147,11 @@ const params = computed(() => ({
pageSize: pageSize.value,
sort: sort.value || 'starred_desc',
categories: undefined,
type: props.type === 'my-collections' ? undefined : props.type,
type: props.type === CollectionTypeEnum.MY_COLLECTIONS ? undefined : props.type,
}));

const { data, isPending, isFetchingNextPage, fetchNextPage, hasNextPage, isSuccess, error, refetch } =
props.type === 'my-collections'
props.type === CollectionTypeEnum.MY_COLLECTIONS
? COLLECTIONS_API_SERVICE.fetchMyCollections(params, user)
: COLLECTIONS_API_SERVICE.fetchCollections(params);

Expand All @@ -161,7 +162,9 @@ const flatData = computed(() =>
),
);

const collectionIds = computed(() => (props.type !== 'my-collections' ? flatData.value.map((c) => c.id) : []));
const collectionIds = computed(() =>
props.type !== CollectionTypeEnum.MY_COLLECTIONS ? flatData.value.map((c) => c.id) : [],
);
useLikeCounts(collectionIds);

const classDisplay = computed(() => {
Expand Down
Loading
Loading