feat: add the fork tag show in community detail page (#11814)

* feat: add the fork tag show in community detail page

* feat: add the isValidated to show under review

* fix: add i18n

* fix: remove the not used params
This commit is contained in:
Shinji-Li
2026-01-26 15:10:23 +08:00
committed by GitHub
parent 0d101dad72
commit cd029eb45b
12 changed files with 189 additions and 44 deletions

View File

@@ -5,6 +5,7 @@
"assistant.like": "Like",
"assistant.likeFailed": "Failed to like",
"assistant.likeSuccess": "Liked",
"assistant.underReview": "Under Review",
"assistant.unfavorite": "Unsave",
"assistant.unfavoriteFailed": "Failed to unsave",
"assistant.unfavoriteSuccess": "Unsaved",
@@ -147,6 +148,7 @@
"fork.success": "Forked successfully!",
"fork.viewAllForks": "View all forks",
"groupAgents.tag": "Group",
"groupAgents.underReview": "Under Review",
"home.communityAgents": "Community Agents",
"home.featuredAssistants": "Featured Agents",
"home.featuredModels": "Featured Models",

View File

@@ -5,6 +5,7 @@
"assistant.like": "点赞",
"assistant.likeFailed": "点赞失败",
"assistant.likeSuccess": "已点赞",
"assistant.underReview": "审核中",
"assistant.unfavorite": "取消收藏",
"assistant.unfavoriteFailed": "取消收藏失败",
"assistant.unfavoriteSuccess": "已取消收藏",
@@ -147,6 +148,7 @@
"fork.success": "派生成功!",
"fork.viewAllForks": "查看所有派生版本",
"groupAgents.tag": "群组",
"groupAgents.underReview": "审核中",
"home.communityAgents": "社区助理",
"home.featuredAssistants": "推荐助理",
"home.featuredModels": "推荐模型",

View File

@@ -56,6 +56,7 @@ export interface DiscoverAssistantItem extends Omit<LobeAgentSettings, 'meta'>,
homepage: string;
identifier: string;
installCount?: number;
isValidated?: boolean;
knowledgeCount: number;
pluginCount: number;
status?: AgentStatus;
@@ -92,6 +93,7 @@ export interface DiscoverAssistantDetail extends DiscoverAssistantItem {
currentVersion?: string;
editorData?: any;
examples?: FewShots;
isValidated?: boolean;
related: DiscoverAssistantItem[];
summary?: string;
versions?: DiscoverAssistantVersion[];

View File

@@ -85,6 +85,10 @@ export interface DiscoverGroupAgentItem extends MetaData {
installCount?: number;
isFeatured?: boolean;
isOfficial?: boolean;
/**
* Whether the agent group is validated
*/
isValidated?: boolean;
/**
* Number of knowledge bases across all member agents
*/
@@ -140,6 +144,10 @@ export interface DiscoverGroupAgentDetail extends DiscoverGroupAgentItem {
* Example conversations (if available from config)
*/
examples?: any;
/**
* Whether the agent group is validated
*/
isValidated?: boolean;
/**
* Member agents in the group
*/

View File

@@ -0,0 +1,59 @@
'use client';
import { Icon, Tag } from '@lobehub/ui';
import { GitFork } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import urlJoin from 'url-join';
import useSWR from 'swr';
import { marketApiService } from '@/services/marketApi';
import { useDetailContext } from './DetailProvider';
/**
* Agent Fork Tag Component
* Displays fork source information if the agent is forked from another agent
*/
const AgentForkTag = memo(() => {
const { t } = useTranslation('discover');
const navigate = useNavigate();
const { identifier, forkedFromAgentId } = useDetailContext();
console.log('forkedFromAgentId', forkedFromAgentId);
// Fetch fork source info
const { data: forkSource } = useSWR(
identifier && forkedFromAgentId ? ['fork-source', identifier] : null,
() => marketApiService.getAgentForkSource(identifier!),
{ revalidateOnFocus: false },
);
if (!forkSource?.source) return null;
const handleClick = () => {
if (forkSource?.source?.identifier) {
navigate(urlJoin('/community/agent', forkSource.source.identifier));
}
};
return (
<Tag
bordered={false}
color="default"
icon={<Icon icon={GitFork} />}
onClick={handleClick}
style={{ cursor: 'pointer' }}
title={t('fork.forkedFrom', {
defaultValue: `Forked from ${forkSource.source.name}`,
})}
>
{t('fork.forkedFrom')}: {forkSource.source.name}
</Tag>
);
});
AgentForkTag.displayName = 'AgentForkTag';
export default AgentForkTag;

View File

@@ -7,6 +7,7 @@ import {
Button,
Flexbox,
Icon,
Tag,
Text,
Tooltip,
TooltipGroup,
@@ -20,7 +21,6 @@ import {
CoinsIcon,
DotIcon,
GitBranchIcon,
GitForkIcon,
HeartIcon,
} from 'lucide-react';
import qs from 'query-string';
@@ -31,12 +31,12 @@ import useSWR from 'swr';
import urlJoin from 'url-join';
import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
import { marketApiService } from '@/services/marketApi';
import { socialService } from '@/services/social';
import { formatIntergerNumber } from '@/utils/format';
import { useCategory } from '../../../(list)/agent/features/Category/useCategory';
import PublishedTime from '../../../../../../../components/PublishedTime';
import AgentForkTag from './AgentForkTag';
import { useDetailContext } from './DetailProvider';
const styles = createStaticStyles(({ css, cssVar }) => ({
@@ -61,7 +61,6 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
knowledgeCount,
userName,
forkCount,
forkedFromAgentId,
} = useDetailContext();
const { mobile = isMobile } = useResponsive();
const { isAuthenticated, signIn, session } = useMarketAuth();
@@ -90,13 +89,6 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
);
const isLiked = likeStatus?.isLiked ?? false;
// Fetch fork source info
const { data: forkSource } = useSWR(
identifier && forkedFromAgentId ? ['fork-source', identifier] : null,
() => marketApiService.getAgentForkSource(identifier!),
{ revalidateOnFocus: false },
);
const handleFavoriteClick = async () => {
if (!isAuthenticated) {
await signIn();
@@ -220,7 +212,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
/>
</Tooltip>
</Flexbox>
<Flexbox align={'center'} gap={4} horizontal>
<Flexbox align={'center'} gap={8} horizontal wrap={'wrap'}>
{author && userName ? (
<Link style={{ color: 'inherit' }} to={urlJoin('/community/user', userName)}>
{author}
@@ -234,32 +226,13 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
date={createdAt as string}
template={'MMM DD, YYYY'}
/>
<AgentForkTag />
{!!forkCount && forkCount > 0 && (
<Tag bordered={false} color="default" icon={<Icon icon={GitBranchIcon} />}>
{forkCount} {t('fork.forks')}
</Tag>
)}
</Flexbox>
{/* Fork information */}
{(forkSource?.source || (forkCount && forkCount > 0)) && (
<Flexbox align={'center'} gap={12} horizontal>
{forkSource?.source && (
<Flexbox align={'center'} gap={6} horizontal>
<Icon icon={GitForkIcon} size={'small'} />
<Text className={styles.time} type={'secondary'}>
{t('fork.forkedFrom')}:{' '}
<Link to={urlJoin('/community/agent', forkSource.source.identifier)}>
{forkSource.source.name}
</Link>
</Text>
</Flexbox>
)}
{forkCount && forkCount > 0 && (
<Flexbox align={'center'} gap={6} horizontal>
<Icon icon={GitBranchIcon} size={'small'} />
<Text className={styles.time} type={'secondary'}>
{forkCount} {t('fork.forks')}
</Text>
</Flexbox>
)}
</Flexbox>
)}
</Flexbox>
</Flexbox>
<TooltipGroup>

View File

@@ -0,0 +1,57 @@
'use client';
import { Icon, Tag } from '@lobehub/ui';
import { GitFork } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import urlJoin from 'url-join';
import useSWR from 'swr';
import { marketApiService } from '@/services/marketApi';
import { useDetailContext } from './DetailProvider';
/**
* Group Agent Fork Tag Component
* Displays fork source information if the group agent is forked from another group agent
*/
const GroupAgentForkTag = memo(() => {
const { t } = useTranslation('discover');
const navigate = useNavigate();
const { identifier, forkedFromGroupId } = useDetailContext();
// Fetch fork source info
const { data: forkSource } = useSWR(
identifier && forkedFromGroupId ? ['group-fork-source', identifier] : null,
() => marketApiService.getAgentGroupForkSource(identifier!),
{ revalidateOnFocus: false },
);
if (!forkSource?.source) return null;
const handleClick = () => {
if (forkSource?.source?.identifier) {
navigate(urlJoin('/community/group_agent', forkSource.source.identifier));
}
};
return (
<Tag
bordered={false}
color="default"
icon={<Icon icon={GitFork} />}
onClick={handleClick}
style={{ cursor: 'pointer' }}
title={t('fork.forkedFrom', {
defaultValue: `Forked from ${forkSource.source.name}`,
})}
>
{t('fork.forkedFrom')}: {forkSource.source.name}
</Tag>
);
});
GroupAgentForkTag.displayName = 'GroupAgentForkTag';
export default GroupAgentForkTag;

View File

@@ -6,13 +6,21 @@ import {
Button,
Flexbox,
Icon,
Tag,
Text,
Tooltip,
TooltipGroup,
} from '@lobehub/ui';
import { App } from 'antd';
import { createStaticStyles, cssVar, useResponsive } from 'antd-style';
import { BookmarkCheckIcon, BookmarkIcon, DotIcon, HeartIcon, UsersIcon } from 'lucide-react';
import {
BookmarkCheckIcon,
BookmarkIcon,
DotIcon,
GitBranchIcon,
HeartIcon,
UsersIcon,
} from 'lucide-react';
import qs from 'query-string';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -25,6 +33,7 @@ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
import { socialService } from '@/services/social';
import { useDetailContext } from './DetailProvider';
import GroupAgentForkTag from './GroupAgentForkTag';
const styles = createStaticStyles(({ css, cssVar }) => ({
time: css`
@@ -51,6 +60,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
identifier,
createdAt,
userName,
forkCount,
} = data;
const displayAvatar = avatar || title?.[0] || '👥';
@@ -199,7 +209,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
/>
</Tooltip>
</Flexbox>
<Flexbox align={'center'} gap={4} horizontal>
<Flexbox align={'center'} gap={8} horizontal wrap={'wrap'}>
{(() => {
// API returns author as object {avatar, name, userName}, but type definition says string
const authorObj =
@@ -220,6 +230,12 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
date={createdAt as string}
template={'MMM DD, YYYY'}
/>
<GroupAgentForkTag />
{!!forkCount && forkCount > 0 && (
<Tag bordered={false} color="default" icon={<Icon icon={GitBranchIcon} />}>
{forkCount} {t('fork.forks')}
</Tag>
)}
</Flexbox>
</Flexbox>
</Flexbox>

View File

@@ -130,6 +130,7 @@ const UserAgentCard = memo<UserAgentCardProps>(
installCount,
status,
identifier,
isValidated,
}) => {
const { t } = useTranslation(['discover', 'setting']);
const navigate = useNavigate();
@@ -307,10 +308,17 @@ const UserAgentCard = memo<UserAgentCardProps>(
{title}
</Text>
</Link>
{isOwner && status && (
<AntTag color={getStatusTagColor(status)} style={{ flexShrink: 0, margin: 0 }}>
{t(`setting:myAgents.status.${status}`)}
{isValidated === false ? (
<AntTag color="orange" style={{ flexShrink: 0, margin: 0 }}>
{t('assistant.underReview', { defaultValue: 'Under Review' })}
</AntTag>
) : (
isOwner &&
status && (
<AntTag color={getStatusTagColor(status)} style={{ flexShrink: 0, margin: 0 }}>
{t(`setting:myAgents.status.${status}`)}
</AntTag>
)
)}
</Flexbox>
</Flexbox>

View File

@@ -120,6 +120,7 @@ const UserGroupCard = memo<UserGroupCardProps>(
identifier,
memberCount,
status,
isValidated,
}) => {
const { t } = useTranslation(['discover', 'setting']);
const navigate = useNavigate();
@@ -235,10 +236,17 @@ const UserGroupCard = memo<UserGroupCardProps>(
{title}
</Text>
</Link>
{isOwner && status && (
<AntTag color={getStatusTagColor(status)} style={{ flexShrink: 0, margin: 0 }}>
{t(`setting:myAgents.status.${status}`)}
{isValidated === false ? (
<AntTag color="orange" style={{ flexShrink: 0, margin: 0 }}>
{t('groupAgents.underReview', { defaultValue: 'Under Review' })}
</AntTag>
) : (
isOwner &&
status && (
<AntTag color={getStatusTagColor(status)} style={{ flexShrink: 0, margin: 0 }}>
{t(`setting:myAgents.status.${status}`)}
</AntTag>
)
)}
</Flexbox>
</Flexbox>

View File

@@ -5,6 +5,7 @@ export default {
'assistant.like': 'Like',
'assistant.likeFailed': 'Failed to like',
'assistant.likeSuccess': 'Liked',
'assistant.underReview': 'Under Review',
'assistant.unfavorite': 'Unsave',
'assistant.unfavoriteFailed': 'Failed to unsave',
'assistant.unfavoriteSuccess': 'Unsaved',
@@ -168,6 +169,8 @@ export default {
'groupAgents.tag': 'Group',
'groupAgents.underReview': 'Under Review',
'home.communityAgents': 'Community Agents',
'home.featuredAssistants': 'Featured Agents',

View File

@@ -577,10 +577,13 @@ export class DiscoverService {
role: example.role || 'user',
}))
: [],
forkCount: (data as any).forkCount,
forkedFromAgentId: (data as any).forkedFromAgentId,
homepage:
(data as any).homepage ||
`https://lobehub.com/discover/assistant/${(data as any).identifier}`,
identifier: (data as any).identifier,
isValidated: (data as any).isValidated,
knowledgeCount:
(data.config as any)?.knowledgeBases?.length || (data as any).knowledgeCount || 0,
pluginCount: (data.config as any)?.plugins?.length || (data as any).pluginCount || 0,
@@ -1725,6 +1728,7 @@ export class DiscoverService {
homepage: `https://lobehub.com/discover/assistant/${agent.identifier}`,
identifier: agent.identifier,
installCount: agent.installCount,
isValidated: agent.isValidated,
knowledgeCount: agent.knowledgeCount || 0,
pluginCount: agent.pluginCount || 0,
schemaVersion: 1,
@@ -1746,6 +1750,7 @@ export class DiscoverService {
installCount: group.installCount || 0,
isFeatured: group.isFeatured || false,
isOfficial: group.isOfficial || false,
isValidated: group.isValidated,
memberCount: 0, // Will be populated from memberAgents in detail view
schemaVersion: 1,
status: group.status,
@@ -1768,6 +1773,7 @@ export class DiscoverService {
homepage: `https://lobehub.com/discover/assistant/${agent.identifier}`,
identifier: agent.identifier,
installCount: agent.installCount,
isValidated: agent.isValidated,
knowledgeCount: agent.knowledgeCount || 0,
pluginCount: agent.pluginCount || 0,
schemaVersion: 1,
@@ -1792,6 +1798,7 @@ export class DiscoverService {
installCount: group.installCount || 0,
isFeatured: group.isFeatured || false,
isOfficial: group.isOfficial || false,
isValidated: group.isValidated,
memberCount: 0, // Will be populated from memberAgents in detail view
schemaVersion: 1,
status: group.status,