diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/MeController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/MeController.java index a9d7926f..f8dbd5b3 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/MeController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/MeController.java @@ -33,12 +33,16 @@ public class MeController extends BaseApiController { public ApiResponse> listMySkills( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String filter, @AuthenticationPrincipal PlatformPrincipal principal) { if (principal == null) { throw new UnauthorizedException("error.auth.required"); } - return ok("response.success.read", mySkillAppService.listMySkills(principal.userId(), page, size)); + return ok( + "response.success.read", + mySkillAppService.listMySkills(principal.userId(), page, size, filter, principal.platformRoles()) + ); } @GetMapping("/stars") diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/MySkillAppService.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/MySkillAppService.java index 98407e53..5821ecee 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/MySkillAppService.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/MySkillAppService.java @@ -16,6 +16,7 @@ import com.iflytek.skillhub.dto.PageResponse; import com.iflytek.skillhub.dto.SkillLifecycleVersionResponse; import com.iflytek.skillhub.dto.SkillSummaryResponse; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; @@ -31,6 +32,15 @@ import java.util.stream.Collectors; */ @Service public class MySkillAppService { + public enum MySkillFilter { + ALL, + PENDING_REVIEW, + PUBLISHED, + REJECTED, + ARCHIVED, + HIDDEN + } + private final SkillRepository skillRepository; private final NamespaceRepository namespaceRepository; private final SkillVersionRepository skillVersionRepository; @@ -54,7 +64,18 @@ public class MySkillAppService { } public PageResponse listMySkills(String userId, int page, int size) { - Page skillPage = skillRepository.findByOwnerId(userId, PageRequest.of(page, size)); + return listMySkills(userId, page, size, null, java.util.Set.of()); + } + + public PageResponse listMySkills(String userId, + int page, + int size, + String filter, + java.util.Set platformRoles) { + MySkillFilter normalizedFilter = parseFilter(filter); + Page skillPage = normalizedFilter == MySkillFilter.ALL + ? skillRepository.findByOwnerId(userId, PageRequest.of(page, size)) + : filterSkillsByLifecycle(userId, page, size, normalizedFilter, platformRoles); List skills = skillPage.getContent(); List namespaceIds = skills.stream() @@ -117,6 +138,9 @@ public class MySkillAppService { currentUserId, Map.of() ); + if (skill.getOwnerId().equals(currentUserId)) { + projection = skillLifecycleProjectionService.projectForOwnerSummary(skill); + } SkillLifecycleProjectionService.VersionProjection headlineVersion = projection.headlineVersion(); SkillLifecycleProjectionService.VersionProjection publishedVersion = projection.publishedVersion(); SkillLifecycleProjectionService.VersionProjection ownerPreviewVersion = projection.ownerPreviewVersion(); @@ -169,4 +193,60 @@ public class MySkillAppService { } return new SkillLifecycleVersionResponse(projection.id(), projection.version(), projection.status()); } + + private Page filterSkillsByLifecycle(String userId, + int page, + int size, + MySkillFilter filter, + java.util.Set platformRoles) { + List skills = skillRepository.findByOwnerId(userId); + List filtered = skills.stream() + .filter(skill -> matchesFilter(skill, filter, platformRoles)) + .toList(); + int fromIndex = Math.min(page * size, filtered.size()); + int toIndex = Math.min(fromIndex + size, filtered.size()); + return new PageImpl<>( + filtered.subList(fromIndex, toIndex), + PageRequest.of(page, size), + filtered.size() + ); + } + + private boolean matchesFilter(Skill skill, MySkillFilter filter, java.util.Set platformRoles) { + if (filter == MySkillFilter.HIDDEN) { + return platformRoles.contains("SUPER_ADMIN") && skill.isHidden(); + } + + if (skill.isHidden()) { + return false; + } + if (filter == MySkillFilter.ARCHIVED) { + return skill.getStatus() == com.iflytek.skillhub.domain.skill.SkillStatus.ARCHIVED; + } + if (skill.getStatus() == com.iflytek.skillhub.domain.skill.SkillStatus.ARCHIVED) { + return false; + } + + SkillLifecycleProjectionService.Projection projection = skillLifecycleProjectionService.projectForOwnerSummary(skill); + SkillLifecycleProjectionService.VersionProjection ownerPreviewVersion = projection.ownerPreviewVersion(); + SkillLifecycleProjectionService.VersionProjection publishedVersion = projection.publishedVersion(); + + return switch (filter) { + case PENDING_REVIEW -> ownerPreviewVersion != null && "PENDING_REVIEW".equals(ownerPreviewVersion.status()); + case PUBLISHED -> publishedVersion != null; + case REJECTED -> ownerPreviewVersion != null && "REJECTED".equals(ownerPreviewVersion.status()); + case ALL, ARCHIVED, HIDDEN -> true; + }; + } + + private MySkillFilter parseFilter(String filter) { + if (filter == null || filter.isBlank()) { + return MySkillFilter.ALL; + } + try { + return MySkillFilter.valueOf(filter.trim().toUpperCase(java.util.Locale.ROOT)); + } catch (IllegalArgumentException ignored) { + return MySkillFilter.ALL; + } + } } diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java index 4ecad2e3..de29b4f7 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java @@ -56,7 +56,7 @@ class MeControllerTest { principal, null, List.of(new SimpleGrantedAuthority("ROLE_USER")) ); - given(mySkillAppService.listMySkills("user-42", 1, 5)) + given(mySkillAppService.listMySkills("user-42", 1, 5, null, Set.of("USER"))) .willReturn(new PageResponse<>( List.of(new SkillSummaryResponse( 7L, @@ -93,6 +93,25 @@ class MeControllerTest { .andExpect(jsonPath("$.data.size").value(5)); } + @Test + void listMySkills_forwardsFilterAndRoles() throws Exception { + PlatformPrincipal principal = new PlatformPrincipal( + "user-42", "tester", "tester@example.com", "", "github", Set.of("SUPER_ADMIN") + ); + var auth = new UsernamePasswordAuthenticationToken( + principal, null, List.of(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN")) + ); + + given(mySkillAppService.listMySkills("user-42", 0, 10, "HIDDEN", Set.of("SUPER_ADMIN"))) + .willReturn(new PageResponse<>(List.of(), 0, 0, 10)); + + mockMvc.perform(get("/api/v1/me/skills") + .with(authentication(auth)) + .param("filter", "HIDDEN")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.total").value(0)); + } + @Test void listMyStars_returns_paginated_items() throws Exception { PlatformPrincipal principal = new PlatformPrincipal( diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/MySkillAppServiceTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/MySkillAppServiceTest.java index af381814..00e28dbe 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/MySkillAppServiceTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/MySkillAppServiceTest.java @@ -6,6 +6,7 @@ import com.iflytek.skillhub.domain.review.PromotionRequest; import com.iflytek.skillhub.domain.review.PromotionRequestRepository; import com.iflytek.skillhub.domain.review.ReviewTaskStatus; import com.iflytek.skillhub.domain.skill.Skill; +import com.iflytek.skillhub.domain.skill.SkillStatus; import com.iflytek.skillhub.domain.skill.SkillRepository; import com.iflytek.skillhub.domain.skill.SkillVersion; import com.iflytek.skillhub.domain.skill.SkillVersionRepository; @@ -27,6 +28,7 @@ import java.math.BigDecimal; import java.time.Instant; import java.util.List; import java.util.Optional; +import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; @@ -94,6 +96,7 @@ class MySkillAppServiceTest { given(skillRepository.findByIdIn(List.of(2L))).willReturn(List.of(secondSkill)); given(skillVersionRepository.findBySkillIdAndStatus(2L, SkillVersionStatus.PUBLISHED)).willReturn(List.of()); + given(skillVersionRepository.findBySkillId(2L)).willReturn(List.of()); given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(new Namespace("team-ai", "Team AI", "user-1"))); var stars = service.listMyStars("user-1", 1, 1); @@ -120,7 +123,7 @@ class MySkillAppServiceTest { given(skillRepository.findByOwnerId("user-1", PageRequest.of(0, 10))) .willReturn(new PageImpl<>(List.of(skill), PageRequest.of(0, 10), 1)); given(skillVersionRepository.findBySkillIdAndStatus(1L, SkillVersionStatus.PUBLISHED)).willReturn(List.of()); - given(skillVersionRepository.findBySkillIdAndStatus(1L, SkillVersionStatus.PENDING_REVIEW)).willReturn(List.of(pendingVersion)); + given(skillVersionRepository.findBySkillId(1L)).willReturn(List.of(pendingVersion)); given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(new Namespace("team-ai", "Team AI", "user-1"))); var skills = service.listMySkills("user-1", 0, 10); @@ -153,6 +156,7 @@ class MySkillAppServiceTest { given(skillRepository.findByOwnerId("user-1", PageRequest.of(0, 10))) .willReturn(new PageImpl<>(List.of(skill), PageRequest.of(0, 10), 1)); given(skillVersionRepository.findBySkillIdAndStatus(2L, SkillVersionStatus.PUBLISHED)).willReturn(List.of(publishedVersion)); + given(skillVersionRepository.findBySkillId(2L)).willReturn(List.of(publishedVersion)); given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(namespace)); given(promotionRequestRepository.findBySourceSkillIdAndStatus(2L, ReviewTaskStatus.PENDING)).willReturn(Optional.empty()); given(promotionRequestRepository.findBySourceSkillIdAndStatus(2L, ReviewTaskStatus.APPROVED)).willReturn(Optional.empty()); @@ -184,6 +188,7 @@ class MySkillAppServiceTest { given(skillRepository.findByOwnerId("user-1", PageRequest.of(0, 10))) .willReturn(new PageImpl<>(List.of(skill), PageRequest.of(0, 10), 1)); given(skillVersionRepository.findBySkillIdAndStatus(2L, SkillVersionStatus.PUBLISHED)).willReturn(List.of(publishedVersion)); + given(skillVersionRepository.findBySkillId(2L)).willReturn(List.of(publishedVersion)); given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(namespace)); given(promotionRequestRepository.findBySourceSkillIdAndStatus(2L, ReviewTaskStatus.PENDING)) .willReturn(Optional.of(new PromotionRequest(2L, 22L, 999L, "user-1"))); @@ -193,4 +198,87 @@ class MySkillAppServiceTest { assertThat(skills.items()).hasSize(1); assertThat(skills.items().get(0).canSubmitPromotion()).isFalse(); } + + @Test + void listMySkills_filtersPendingReviewForRegularUsers() { + Skill pendingSkill = createSkill(1L, 101L, "pending-skill", "user-1"); + SkillVersion pendingVersion = createVersion(1L, 11L, "1.0.0", SkillVersionStatus.PENDING_REVIEW, "2026-03-15T09:30:00Z"); + Skill publishedSkill = createSkill(2L, 101L, "published-skill", "user-1"); + SkillVersion publishedVersion = createVersion(2L, 22L, "1.2.0", SkillVersionStatus.PUBLISHED, "2026-03-15T10:30:00Z"); + + given(skillRepository.findByOwnerId("user-1")).willReturn(List.of(pendingSkill, publishedSkill)); + given(skillVersionRepository.findBySkillId(1L)).willReturn(List.of(pendingVersion)); + given(skillVersionRepository.findBySkillId(2L)).willReturn(List.of(publishedVersion)); + given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(namespace(101L, "team-ai"))); + + var result = service.listMySkills("user-1", 0, 10, "PENDING_REVIEW", Set.of("USER")); + + assertThat(result.total()).isEqualTo(1); + assertThat(result.items()).extracting("slug").containsExactly("pending-skill"); + } + + @Test + void listMySkills_filtersHiddenOnlyForSuperAdmins() { + Skill hiddenSkill = createSkill(3L, 101L, "hidden-skill", "user-1"); + hiddenSkill.setHidden(true); + Skill publishedSkill = createSkill(4L, 101L, "published-skill", "user-1"); + SkillVersion hiddenVersion = createVersion(3L, 33L, "1.0.0", SkillVersionStatus.PUBLISHED, "2026-03-15T09:30:00Z"); + + given(skillRepository.findByOwnerId("user-1")).willReturn(List.of(hiddenSkill, publishedSkill)); + given(skillVersionRepository.findBySkillId(3L)).willReturn(List.of(hiddenVersion)); + given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(namespace(101L, "team-ai"))); + + var regularUserResult = service.listMySkills("user-1", 0, 10, "HIDDEN", Set.of("USER")); + var superAdminResult = service.listMySkills("user-1", 0, 10, "HIDDEN", Set.of("SUPER_ADMIN")); + + assertThat(regularUserResult.total()).isZero(); + assertThat(superAdminResult.total()).isEqualTo(1); + assertThat(superAdminResult.items()).extracting("slug").containsExactly("hidden-skill"); + } + + @Test + void listMySkills_exposesRejectedOwnerPreviewInSummary() { + Skill skill = createSkill(5L, 101L, "rejected-skill", "user-1"); + SkillVersion rejectedVersion = createVersion(5L, 55L, "1.1.0", SkillVersionStatus.REJECTED, "2026-03-15T09:30:00Z"); + + given(skillRepository.findByOwnerId("user-1", PageRequest.of(0, 10))) + .willReturn(new PageImpl<>(List.of(skill), PageRequest.of(0, 10), 1)); + given(skillVersionRepository.findBySkillId(5L)).willReturn(List.of(rejectedVersion)); + given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(namespace(101L, "team-ai"))); + + var result = service.listMySkills("user-1", 0, 10); + + assertThat(result.items()).hasSize(1); + assertThat(result.items().get(0).ownerPreviewVersion()).isNotNull(); + assertThat(result.items().get(0).ownerPreviewVersion().status()).isEqualTo("REJECTED"); + assertThat(result.items().get(0).headlineVersion().status()).isEqualTo("REJECTED"); + } + + private Skill createSkill(Long id, Long namespaceId, String slug, String ownerId) { + Skill skill = new Skill(namespaceId, slug, ownerId, SkillVisibility.PUBLIC); + skill.setDisplayName(slug); + ReflectionTestUtils.setField(skill, "id", id); + ReflectionTestUtils.setField(skill, "namespaceId", namespaceId); + ReflectionTestUtils.setField(skill, "updatedAt", Instant.parse("2026-03-15T11:00:00Z")); + ReflectionTestUtils.setField(skill, "ratingAvg", BigDecimal.ZERO); + ReflectionTestUtils.setField(skill, "ratingCount", 0); + ReflectionTestUtils.setField(skill, "downloadCount", 0L); + ReflectionTestUtils.setField(skill, "starCount", 0); + skill.setStatus(SkillStatus.ACTIVE); + return skill; + } + + private SkillVersion createVersion(Long skillId, Long id, String version, SkillVersionStatus status, String createdAt) { + SkillVersion skillVersion = new SkillVersion(skillId, version, "user-1"); + skillVersion.setStatus(status); + ReflectionTestUtils.setField(skillVersion, "id", id); + ReflectionTestUtils.setField(skillVersion, "createdAt", Instant.parse(createdAt)); + return skillVersion; + } + + private Namespace namespace(Long id, String slug) { + Namespace namespace = new Namespace(slug, slug, "user-1"); + ReflectionTestUtils.setField(namespace, "id", id); + return namespace; + } } diff --git a/web/src/api/client.ts b/web/src/api/client.ts index 3b7092bd..650f792d 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -845,10 +845,13 @@ export const governanceApi = { } export const meApi = { - async getSkills(params?: { page?: number; size?: number }): Promise<{ items: SkillSummary[]; total: number; page: number; size: number }> { + async getSkills(params?: { page?: number; size?: number; filter?: string }): Promise<{ items: SkillSummary[]; total: number; page: number; size: number }> { const searchParams = new URLSearchParams() searchParams.set('page', String(params?.page ?? 0)) searchParams.set('size', String(params?.size ?? 10)) + if (params?.filter) { + searchParams.set('filter', params.filter) + } return fetchJson<{ items: SkillSummary[]; total: number; page: number; size: number }>(`${WEB_API_PREFIX}/me/skills?${searchParams.toString()}`) }, diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 7b2f5794..c39890b3 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -295,6 +295,14 @@ "mySkills": { "title": "My Skills", "subtitle": "Manage your published skills", + "filters": { + "ALL": "All", + "PENDING_REVIEW": "Pending Review", + "PUBLISHED": "Published", + "REJECTED": "Rejected", + "ARCHIVED": "Archived", + "HIDDEN": "Hidden" + }, "publishNew": "Publish New Skill", "archive": "Archive", "unarchive": "Restore", @@ -302,6 +310,7 @@ "statusPendingReview": "Pending Review", "statusPublished": "Published", "statusRejected": "Rejected", + "statusHidden": "Hidden", "archiveConfirmTitle": "Archive skill", "archiveConfirmDescription": "After archiving, regular users will no longer be able to view or download \"{{skill}}\". Continue?", "unarchiveConfirmTitle": "Restore skill", diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json index ff5dcc9a..28704701 100644 --- a/web/src/i18n/locales/zh.json +++ b/web/src/i18n/locales/zh.json @@ -295,6 +295,14 @@ "mySkills": { "title": "我的技能", "subtitle": "管理你发布的技能", + "filters": { + "ALL": "全部", + "PENDING_REVIEW": "待审核", + "PUBLISHED": "已发布", + "REJECTED": "已拒绝", + "ARCHIVED": "已归档", + "HIDDEN": "已隐藏" + }, "publishNew": "发布新技能", "archive": "归档", "unarchive": "恢复", @@ -302,6 +310,7 @@ "statusPendingReview": "审核中", "statusPublished": "已发布", "statusRejected": "已拒绝", + "statusHidden": "已隐藏", "archiveConfirmTitle": "确认归档技能", "archiveConfirmDescription": "归档后普通用户将无法看到或下载“{{skill}}”,确定继续吗?", "unarchiveConfirmTitle": "确认恢复技能", diff --git a/web/src/pages/dashboard/my-skill-filters.test.ts b/web/src/pages/dashboard/my-skill-filters.test.ts new file mode 100644 index 00000000..0d830998 --- /dev/null +++ b/web/src/pages/dashboard/my-skill-filters.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from 'vitest' +import { getMySkillFilters } from './my-skill-filters' + +describe('getMySkillFilters', () => { + it('returns lifecycle filters for regular users', () => { + expect(getMySkillFilters(false)).toEqual(['ALL', 'PENDING_REVIEW', 'PUBLISHED', 'REJECTED', 'ARCHIVED']) + }) + + it('adds hidden filter for super admins', () => { + expect(getMySkillFilters(true)).toEqual(['ALL', 'PUBLISHED', 'HIDDEN', 'ARCHIVED']) + }) +}) diff --git a/web/src/pages/dashboard/my-skill-filters.ts b/web/src/pages/dashboard/my-skill-filters.ts new file mode 100644 index 00000000..2f38a05c --- /dev/null +++ b/web/src/pages/dashboard/my-skill-filters.ts @@ -0,0 +1,9 @@ +export type MySkillFilter = 'ALL' | 'PENDING_REVIEW' | 'PUBLISHED' | 'REJECTED' | 'ARCHIVED' | 'HIDDEN' + +export function getMySkillFilters(isSuperAdmin: boolean): MySkillFilter[] { + if (isSuperAdmin) { + return ['ALL', 'PUBLISHED', 'HIDDEN', 'ARCHIVED'] + } + return ['ALL', 'PENDING_REVIEW', 'PUBLISHED', 'REJECTED', 'ARCHIVED'] +} + diff --git a/web/src/pages/dashboard/my-skills.tsx b/web/src/pages/dashboard/my-skills.tsx index a4b150b7..0aa4f2d2 100644 --- a/web/src/pages/dashboard/my-skills.tsx +++ b/web/src/pages/dashboard/my-skills.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { useNavigate } from '@tanstack/react-router' import { useTranslation } from 'react-i18next' +import { useAuth } from '@/features/auth/use-auth' import { Button } from '@/shared/ui/button' import { Card } from '@/shared/ui/card' import { EmptyState } from '@/shared/components/empty-state' @@ -12,6 +13,7 @@ import { getHeadlineVersion, getPublishedVersion, getOwnerPreviewVersion, hasPen import { formatCompactCount } from '@/shared/lib/number-format' import { toast } from '@/shared/lib/toast' import { ApiError } from '@/api/client' +import { getMySkillFilters, type MySkillFilter } from './my-skill-filters' const PAGE_SIZE = 10 @@ -34,14 +36,17 @@ function getPromotionConflictKey(error: ApiError): 'promotion.duplicate_pending' export function MySkillsPage() { const navigate = useNavigate() const { t } = useTranslation() + const { hasRole } = useAuth() const [page, setPage] = useState(0) + const [filter, setFilter] = useState('ALL') const [archiveTarget, setArchiveTarget] = useState<{ namespace: string; slug: string; name: string } | null>(null) const [unarchiveTarget, setUnarchiveTarget] = useState<{ namespace: string; slug: string; name: string } | null>(null) const [withdrawTarget, setWithdrawTarget] = useState<{ namespace: string; slug: string; name: string; version: string } | null>(null) const [promotionTarget, setPromotionTarget] = useState<{ skillId: number; versionId: number; name: string; version: string } | null>(null) - const { data: skillPage, isLoading } = useMySkills({ page, size: PAGE_SIZE }) + const { data: skillPage, isLoading } = useMySkills({ page, size: PAGE_SIZE, filter: filter === 'ALL' ? undefined : filter }) const skills = skillPage?.items ?? [] const totalPages = skillPage ? Math.max(Math.ceil(skillPage.total / skillPage.size), 1) : 1 + const availableFilters = getMySkillFilters(hasRole('SUPER_ADMIN')) const archiveMutation = useArchiveSkill() const unarchiveMutation = useUnarchiveSkill() const withdrawMutation = useWithdrawSkillReview() @@ -55,6 +60,9 @@ export function MySkillsPage() { } const resolveStatusLabel = (status?: string) => { + if (status === 'HIDDEN') { + return t('mySkills.statusHidden') + } if (status === 'ARCHIVED') { return t('mySkills.statusArchived') } @@ -71,6 +79,9 @@ export function MySkillsPage() { } const resolveStatusClassName = (status?: string) => { + if (status === 'HIDDEN') { + return 'status-pill status-pill--archived' + } if (status === 'ARCHIVED') { return 'status-pill status-pill--archived' } @@ -200,6 +211,23 @@ export function MySkillsPage() { )} /> +
+ {availableFilters.map((option) => ( + + ))} +
+ {skillPage && skillPage.total > 0 ? ( <>
@@ -235,9 +263,9 @@ export function MySkillsPage() { {formatCompactCount(skill.downloadCount)} - {skill.status ? ( - - {resolveStatusLabel(skill.status)} + {skill.status || filter === 'HIDDEN' ? ( + + {resolveStatusLabel(filter === 'HIDDEN' ? 'HIDDEN' : skill.status)} ) : null} {headlineVersion?.status ? ( diff --git a/web/src/shared/hooks/use-skill-queries.ts b/web/src/shared/hooks/use-skill-queries.ts index d9d46b83..c04b47b2 100644 --- a/web/src/shared/hooks/use-skill-queries.ts +++ b/web/src/shared/hooks/use-skill-queries.ts @@ -42,7 +42,7 @@ async function getSkillDocumentation(namespace: string, slug: string, version: s return fetchText(`${WEB_API_PREFIX}/skills/${cleanNamespace}/${slug}/versions/${version}/file?path=${encodeURIComponent(path)}`) } -async function getMySkills(params: { page?: number; size?: number } = {}): Promise> { +async function getMySkills(params: { page?: number; size?: number; filter?: string } = {}): Promise> { return meApi.getSkills(params) } @@ -159,7 +159,7 @@ export function useSkillVersionDetail(namespace: string, slug: string, version?: }) } -export function useMySkills(params: { page?: number; size?: number } = {}) { +export function useMySkills(params: { page?: number; size?: number; filter?: string } = {}) { return useQuery({ queryKey: ['skills', 'my', params], queryFn: () => getMySkills(params),