@lobehub/lobehub 2.0.0-next.296 → 2.0.0-next.297
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +2 -2
- package/packages/types/package.json +1 -1
- package/packages/types/src/discover/assistants.ts +4 -0
- package/packages/types/src/discover/groupAgents.ts +196 -0
- package/packages/types/src/discover/index.ts +5 -1
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/DetailProvider.tsx +19 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Members/index.tsx +137 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Nav.tsx +88 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Overview/index.tsx +213 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Related/index.tsx +85 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/SystemRole/TagList.tsx +20 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/SystemRole/index.tsx +71 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Versions/index.tsx +119 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/index.tsx +51 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Header.tsx +253 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/AddGroupAgent.tsx +222 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/index.tsx +34 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/Summary/index.tsx +42 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/index.tsx +41 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/StatusPage/index.tsx +104 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/index.tsx +103 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/loading.tsx +1 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +7 -1
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserContent.tsx +2 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupCard.tsx +186 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupList.tsx +59 -0
- package/src/app/[variants]/(main)/community/(detail)/user/index.tsx +3 -1
- package/src/app/[variants]/(main)/community/(list)/assistant/features/List/Item.tsx +26 -8
- package/src/app/[variants]/(main)/community/(list)/assistant/index.tsx +1 -0
- package/src/app/[variants]/(main)/group/profile/features/GroupProfile/index.tsx +2 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/AgentPublishButton/PublishResultModal.tsx +2 -1
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/GroupForkConfirmModal.tsx +60 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/GroupPublishResultModal.tsx +62 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/PublishButton.tsx +122 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/index.tsx +46 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/types.ts +12 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/useMarketGroupPublish.ts +211 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/utils.ts +22 -0
- package/src/app/[variants]/router/desktopRouter.config.tsx +7 -0
- package/src/locales/default/setting.ts +12 -0
- package/src/server/routers/lambda/market/agentGroup.ts +296 -0
- package/src/server/routers/lambda/market/index.ts +134 -4
- package/src/server/services/discover/index.ts +123 -7
- package/src/services/discover.ts +55 -0
- package/src/store/discover/slices/groupAgent/action.ts +80 -0
- package/src/store/discover/store.ts +3 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ActionIcon,
|
|
5
|
+
Avatar,
|
|
6
|
+
Button,
|
|
7
|
+
Flexbox,
|
|
8
|
+
Icon,
|
|
9
|
+
Text,
|
|
10
|
+
Tooltip,
|
|
11
|
+
TooltipGroup,
|
|
12
|
+
} from '@lobehub/ui';
|
|
13
|
+
import { App } from 'antd';
|
|
14
|
+
import { createStaticStyles, cssVar, useResponsive } from 'antd-style';
|
|
15
|
+
import { BookmarkCheckIcon, BookmarkIcon, DotIcon, HeartIcon, UsersIcon } from 'lucide-react';
|
|
16
|
+
import qs from 'query-string';
|
|
17
|
+
import { memo, useState } from 'react';
|
|
18
|
+
import { useTranslation } from 'react-i18next';
|
|
19
|
+
import { Link } from 'react-router-dom';
|
|
20
|
+
import useSWR from 'swr';
|
|
21
|
+
import urlJoin from 'url-join';
|
|
22
|
+
|
|
23
|
+
import PublishedTime from '@/components/PublishedTime';
|
|
24
|
+
import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
|
|
25
|
+
import { socialService } from '@/services/social';
|
|
26
|
+
|
|
27
|
+
import { useDetailContext } from './DetailProvider';
|
|
28
|
+
|
|
29
|
+
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
30
|
+
time: css`
|
|
31
|
+
font-size: 12px;
|
|
32
|
+
color: ${cssVar.colorTextDescription};
|
|
33
|
+
`,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
|
37
|
+
const { t } = useTranslation('discover');
|
|
38
|
+
const { message } = App.useApp();
|
|
39
|
+
const data = useDetailContext();
|
|
40
|
+
const { mobile = isMobile } = useResponsive();
|
|
41
|
+
const { isAuthenticated, signIn, session } = useMarketAuth();
|
|
42
|
+
const [favoriteLoading, setFavoriteLoading] = useState(false);
|
|
43
|
+
const [likeLoading, setLikeLoading] = useState(false);
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
memberAgents = [],
|
|
47
|
+
author,
|
|
48
|
+
avatar,
|
|
49
|
+
title,
|
|
50
|
+
category,
|
|
51
|
+
identifier,
|
|
52
|
+
createdAt,
|
|
53
|
+
userName,
|
|
54
|
+
} = data;
|
|
55
|
+
|
|
56
|
+
const displayAvatar = avatar || title?.[0] || '👥';
|
|
57
|
+
const memberCount = memberAgents?.length || 0;
|
|
58
|
+
|
|
59
|
+
// Set access token for social service
|
|
60
|
+
if (session?.accessToken) {
|
|
61
|
+
socialService.setAccessToken(session.accessToken);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// TODO: Use 'group_agent' type when social service supports it
|
|
65
|
+
// Fetch favorite status
|
|
66
|
+
const { data: favoriteStatus, mutate: mutateFavorite } = useSWR(
|
|
67
|
+
identifier && isAuthenticated ? ['favorite-status', 'agent', identifier] : null,
|
|
68
|
+
() => socialService.checkFavoriteStatus('agent', identifier!),
|
|
69
|
+
{ revalidateOnFocus: false },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const isFavorited = favoriteStatus?.isFavorited ?? false;
|
|
73
|
+
|
|
74
|
+
// Fetch like status
|
|
75
|
+
const { data: likeStatus, mutate: mutateLike } = useSWR(
|
|
76
|
+
identifier && isAuthenticated ? ['like-status', 'agent', identifier] : null,
|
|
77
|
+
() => socialService.checkLikeStatus('agent', identifier!),
|
|
78
|
+
{ revalidateOnFocus: false },
|
|
79
|
+
);
|
|
80
|
+
const isLiked = likeStatus?.isLiked ?? false;
|
|
81
|
+
|
|
82
|
+
const handleFavoriteClick = async () => {
|
|
83
|
+
if (!isAuthenticated) {
|
|
84
|
+
await signIn();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!identifier) return;
|
|
89
|
+
|
|
90
|
+
setFavoriteLoading(true);
|
|
91
|
+
try {
|
|
92
|
+
if (isFavorited) {
|
|
93
|
+
await socialService.removeFavorite('agent', identifier);
|
|
94
|
+
message.success(t('assistant.unfavoriteSuccess'));
|
|
95
|
+
} else {
|
|
96
|
+
await socialService.addFavorite('agent', identifier);
|
|
97
|
+
message.success(t('assistant.favoriteSuccess'));
|
|
98
|
+
}
|
|
99
|
+
await mutateFavorite();
|
|
100
|
+
} catch {
|
|
101
|
+
message.error(t('assistant.favoriteFailed'));
|
|
102
|
+
} finally {
|
|
103
|
+
setFavoriteLoading(false);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const handleLikeClick = async () => {
|
|
108
|
+
if (!isAuthenticated) {
|
|
109
|
+
await signIn();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!identifier) return;
|
|
114
|
+
|
|
115
|
+
setLikeLoading(true);
|
|
116
|
+
try {
|
|
117
|
+
if (isLiked) {
|
|
118
|
+
await socialService.unlike('agent', identifier);
|
|
119
|
+
message.success(t('assistant.unlikeSuccess'));
|
|
120
|
+
} else {
|
|
121
|
+
await socialService.like('agent', identifier);
|
|
122
|
+
message.success(t('assistant.likeSuccess'));
|
|
123
|
+
}
|
|
124
|
+
await mutateLike();
|
|
125
|
+
} catch {
|
|
126
|
+
message.error(t('assistant.likeFailed'));
|
|
127
|
+
} finally {
|
|
128
|
+
setLikeLoading(false);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const cateButton = category ? (
|
|
133
|
+
<Link
|
|
134
|
+
to={qs.stringifyUrl({
|
|
135
|
+
query: { category },
|
|
136
|
+
url: '/community/group_agent',
|
|
137
|
+
})}
|
|
138
|
+
>
|
|
139
|
+
<Button size={'middle'} variant={'outlined'}>
|
|
140
|
+
{category}
|
|
141
|
+
</Button>
|
|
142
|
+
</Link>
|
|
143
|
+
) : null;
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Flexbox gap={12}>
|
|
147
|
+
<Flexbox align={'flex-start'} gap={16} horizontal width={'100%'}>
|
|
148
|
+
<Avatar avatar={displayAvatar} shape={'square'} size={mobile ? 48 : 64} />
|
|
149
|
+
<Flexbox
|
|
150
|
+
flex={1}
|
|
151
|
+
gap={4}
|
|
152
|
+
style={{
|
|
153
|
+
overflow: 'hidden',
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
<Flexbox
|
|
157
|
+
align={'center'}
|
|
158
|
+
gap={8}
|
|
159
|
+
horizontal
|
|
160
|
+
justify={'space-between'}
|
|
161
|
+
style={{
|
|
162
|
+
overflow: 'hidden',
|
|
163
|
+
position: 'relative',
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
<Flexbox
|
|
167
|
+
align={'center'}
|
|
168
|
+
flex={1}
|
|
169
|
+
gap={12}
|
|
170
|
+
horizontal
|
|
171
|
+
style={{
|
|
172
|
+
overflow: 'hidden',
|
|
173
|
+
position: 'relative',
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
<Text
|
|
177
|
+
as={'h1'}
|
|
178
|
+
ellipsis
|
|
179
|
+
style={{ fontSize: mobile ? 18 : 24, margin: 0 }}
|
|
180
|
+
title={identifier}
|
|
181
|
+
>
|
|
182
|
+
{title}
|
|
183
|
+
</Text>
|
|
184
|
+
</Flexbox>
|
|
185
|
+
<Tooltip title={isLiked ? t('assistant.unlike') : t('assistant.like')}>
|
|
186
|
+
<ActionIcon
|
|
187
|
+
icon={HeartIcon}
|
|
188
|
+
loading={likeLoading}
|
|
189
|
+
onClick={handleLikeClick}
|
|
190
|
+
style={isLiked ? { color: '#ff4d4f' } : undefined}
|
|
191
|
+
/>
|
|
192
|
+
</Tooltip>
|
|
193
|
+
<Tooltip title={isFavorited ? t('assistant.unfavorite') : t('assistant.favorite')}>
|
|
194
|
+
<ActionIcon
|
|
195
|
+
icon={isFavorited ? BookmarkCheckIcon : BookmarkIcon}
|
|
196
|
+
loading={favoriteLoading}
|
|
197
|
+
onClick={handleFavoriteClick}
|
|
198
|
+
variant={isFavorited ? 'outlined' : undefined}
|
|
199
|
+
/>
|
|
200
|
+
</Tooltip>
|
|
201
|
+
</Flexbox>
|
|
202
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
|
203
|
+
{(() => {
|
|
204
|
+
// API returns author as object {avatar, name, userName}, but type definition says string
|
|
205
|
+
const authorObj =
|
|
206
|
+
typeof author === 'object' && author !== null ? (author as any) : null;
|
|
207
|
+
const authorName = authorObj ? authorObj.name || authorObj.userName : author;
|
|
208
|
+
|
|
209
|
+
return authorName && userName ? (
|
|
210
|
+
<Link style={{ color: 'inherit' }} to={urlJoin('/community/user', userName)}>
|
|
211
|
+
{authorName}
|
|
212
|
+
</Link>
|
|
213
|
+
) : (
|
|
214
|
+
authorName
|
|
215
|
+
);
|
|
216
|
+
})()}
|
|
217
|
+
<Icon icon={DotIcon} />
|
|
218
|
+
<PublishedTime
|
|
219
|
+
className={styles.time}
|
|
220
|
+
date={createdAt as string}
|
|
221
|
+
template={'MMM DD, YYYY'}
|
|
222
|
+
/>
|
|
223
|
+
</Flexbox>
|
|
224
|
+
</Flexbox>
|
|
225
|
+
</Flexbox>
|
|
226
|
+
<TooltipGroup>
|
|
227
|
+
<Flexbox
|
|
228
|
+
align={'center'}
|
|
229
|
+
gap={mobile ? 12 : 24}
|
|
230
|
+
horizontal
|
|
231
|
+
style={{
|
|
232
|
+
color: cssVar.colorTextSecondary,
|
|
233
|
+
}}
|
|
234
|
+
>
|
|
235
|
+
{!mobile && cateButton}
|
|
236
|
+
{Boolean(memberCount) && (
|
|
237
|
+
<Tooltip
|
|
238
|
+
styles={{ root: { pointerEvents: 'none' } }}
|
|
239
|
+
title={t('groupAgents.memberCount', { defaultValue: 'Members' })}
|
|
240
|
+
>
|
|
241
|
+
<Flexbox align={'center'} gap={6} horizontal>
|
|
242
|
+
<Icon icon={UsersIcon} />
|
|
243
|
+
{memberCount}
|
|
244
|
+
</Flexbox>
|
|
245
|
+
</Tooltip>
|
|
246
|
+
)}
|
|
247
|
+
</Flexbox>
|
|
248
|
+
</TooltipGroup>
|
|
249
|
+
</Flexbox>
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
export default Header;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Button, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
|
|
4
|
+
import { App } from 'antd';
|
|
5
|
+
import { createStaticStyles } from 'antd-style';
|
|
6
|
+
import { ChevronDownIcon } from 'lucide-react';
|
|
7
|
+
import { memo, useState } from 'react';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { useNavigate } from 'react-router-dom';
|
|
10
|
+
import urlJoin from 'url-join';
|
|
11
|
+
|
|
12
|
+
import { chatGroupService } from '@/services/chatGroup';
|
|
13
|
+
import { discoverService } from '@/services/discover';
|
|
14
|
+
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
15
|
+
|
|
16
|
+
import { useDetailContext } from '../../DetailProvider';
|
|
17
|
+
|
|
18
|
+
const styles = createStaticStyles(({ css }) => ({
|
|
19
|
+
buttonGroup: css`
|
|
20
|
+
width: 100%;
|
|
21
|
+
`,
|
|
22
|
+
menuButton: css`
|
|
23
|
+
padding-inline: 8px;
|
|
24
|
+
border-start-start-radius: 0 !important;
|
|
25
|
+
border-end-start-radius: 0 !important;
|
|
26
|
+
`,
|
|
27
|
+
primaryButton: css`
|
|
28
|
+
border-start-end-radius: 0 !important;
|
|
29
|
+
border-end-end-radius: 0 !important;
|
|
30
|
+
`,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const AddGroupAgent = memo<{ mobile?: boolean }>(() => {
|
|
34
|
+
const {
|
|
35
|
+
avatar,
|
|
36
|
+
description,
|
|
37
|
+
tags,
|
|
38
|
+
title,
|
|
39
|
+
config,
|
|
40
|
+
backgroundColor,
|
|
41
|
+
identifier,
|
|
42
|
+
memberAgents = [],
|
|
43
|
+
} = useDetailContext();
|
|
44
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
45
|
+
const { message, modal } = App.useApp();
|
|
46
|
+
const { t } = useTranslation('discover');
|
|
47
|
+
const navigate = useNavigate();
|
|
48
|
+
const loadGroups = useAgentGroupStore((s) => s.loadGroups);
|
|
49
|
+
|
|
50
|
+
const meta = {
|
|
51
|
+
avatar,
|
|
52
|
+
backgroundColor,
|
|
53
|
+
description,
|
|
54
|
+
tags,
|
|
55
|
+
title,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Check if a group with the same title already exists
|
|
59
|
+
const checkDuplicateGroup = async () => {
|
|
60
|
+
if (!title) return false;
|
|
61
|
+
try {
|
|
62
|
+
const groups = await chatGroupService.getGroups();
|
|
63
|
+
return groups.some((g) => g.title === title);
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const showDuplicateConfirmation = (callback: () => void) => {
|
|
70
|
+
modal.confirm({
|
|
71
|
+
cancelText: t('cancel', { ns: 'common' }),
|
|
72
|
+
content: t('groupAgents.duplicateAdd.content', {
|
|
73
|
+
defaultValue: 'This group agent has already been added. Do you want to add it again?',
|
|
74
|
+
title,
|
|
75
|
+
}),
|
|
76
|
+
okText: t('groupAgents.duplicateAdd.ok', { defaultValue: 'Add Anyway' }),
|
|
77
|
+
onOk: callback,
|
|
78
|
+
title: t('groupAgents.duplicateAdd.title', { defaultValue: 'Group Already Added' }),
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const createGroupFromMarket = async (shouldNavigate = true) => {
|
|
83
|
+
if (!config) {
|
|
84
|
+
message.error(
|
|
85
|
+
t('groupAgents.noConfig', { defaultValue: 'Group configuration not available' }),
|
|
86
|
+
);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Prepare group config
|
|
91
|
+
const groupConfig = {
|
|
92
|
+
config: {
|
|
93
|
+
allowDM: config.allowDM,
|
|
94
|
+
openingMessage: config.openingMessage,
|
|
95
|
+
openingQuestions: config.openingQuestions,
|
|
96
|
+
revealDM: config.revealDM,
|
|
97
|
+
},
|
|
98
|
+
content: config.systemRole,
|
|
99
|
+
...meta,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Prepare member agents from market data
|
|
103
|
+
const members = memberAgents.map((member: any) => {
|
|
104
|
+
const agent = member.agent || member;
|
|
105
|
+
const currentVersion = member.currentVersion || member;
|
|
106
|
+
return {
|
|
107
|
+
avatar: currentVersion.avatar,
|
|
108
|
+
backgroundColor: currentVersion.backgroundColor,
|
|
109
|
+
description: currentVersion.description,
|
|
110
|
+
model: currentVersion.model,
|
|
111
|
+
plugins: currentVersion.plugins,
|
|
112
|
+
provider: currentVersion.provider,
|
|
113
|
+
systemRole: currentVersion.systemRole || currentVersion.content,
|
|
114
|
+
tags: currentVersion.tags,
|
|
115
|
+
title: currentVersion.name || agent.name,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// Create group with all members in one request
|
|
121
|
+
const result = await chatGroupService.createGroupWithMembers(groupConfig, members);
|
|
122
|
+
|
|
123
|
+
// Refresh group list
|
|
124
|
+
await loadGroups();
|
|
125
|
+
|
|
126
|
+
// Report installation to marketplace
|
|
127
|
+
if (identifier) {
|
|
128
|
+
discoverService.reportAgentInstall(identifier);
|
|
129
|
+
discoverService.reportAgentEvent({
|
|
130
|
+
event: 'add',
|
|
131
|
+
identifier,
|
|
132
|
+
source: location.pathname,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
message.success(
|
|
137
|
+
t('groupAgents.addSuccess', { defaultValue: 'Group agent added successfully!' }),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (shouldNavigate) {
|
|
141
|
+
navigate(urlJoin('/group', result.groupId));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Failed to create group from market:', error);
|
|
147
|
+
message.error(
|
|
148
|
+
t('groupAgents.addError', {
|
|
149
|
+
defaultValue: 'Failed to add group agent. Please try again.',
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const handleAddAndConverse = async () => {
|
|
157
|
+
setIsLoading(true);
|
|
158
|
+
try {
|
|
159
|
+
const isDuplicate = await checkDuplicateGroup();
|
|
160
|
+
if (isDuplicate) {
|
|
161
|
+
showDuplicateConfirmation(() => createGroupFromMarket(true));
|
|
162
|
+
} else {
|
|
163
|
+
await createGroupFromMarket(true);
|
|
164
|
+
}
|
|
165
|
+
} finally {
|
|
166
|
+
setIsLoading(false);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const handleAdd = async () => {
|
|
171
|
+
setIsLoading(true);
|
|
172
|
+
try {
|
|
173
|
+
const isDuplicate = await checkDuplicateGroup();
|
|
174
|
+
if (isDuplicate) {
|
|
175
|
+
showDuplicateConfirmation(() => createGroupFromMarket(false));
|
|
176
|
+
} else {
|
|
177
|
+
await createGroupFromMarket(false);
|
|
178
|
+
}
|
|
179
|
+
} finally {
|
|
180
|
+
setIsLoading(false);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const menuItems = [
|
|
185
|
+
{
|
|
186
|
+
key: 'addGroup',
|
|
187
|
+
label: t('groupAgents.addGroup', { defaultValue: 'Add Group' }),
|
|
188
|
+
onClick: handleAdd,
|
|
189
|
+
},
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Flexbox className={styles.buttonGroup} gap={0} horizontal>
|
|
194
|
+
<Button
|
|
195
|
+
block
|
|
196
|
+
className={styles.primaryButton}
|
|
197
|
+
loading={isLoading}
|
|
198
|
+
onClick={handleAddAndConverse}
|
|
199
|
+
size={'large'}
|
|
200
|
+
style={{ flex: 1, width: 'unset' }}
|
|
201
|
+
type={'primary'}
|
|
202
|
+
>
|
|
203
|
+
{t('groupAgents.addAndConverse', { defaultValue: 'Add & Start Conversation' })}
|
|
204
|
+
</Button>
|
|
205
|
+
<DropdownMenu
|
|
206
|
+
items={menuItems}
|
|
207
|
+
popupProps={{ style: { minWidth: 267 } }}
|
|
208
|
+
triggerProps={{ disabled: isLoading }}
|
|
209
|
+
>
|
|
210
|
+
<Button
|
|
211
|
+
className={styles.menuButton}
|
|
212
|
+
disabled={isLoading}
|
|
213
|
+
icon={<Icon icon={ChevronDownIcon} />}
|
|
214
|
+
size={'large'}
|
|
215
|
+
type={'primary'}
|
|
216
|
+
/>
|
|
217
|
+
</DropdownMenu>
|
|
218
|
+
</Flexbox>
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
export default AddGroupAgent;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import urlJoin from 'url-join';
|
|
6
|
+
|
|
7
|
+
import { OFFICIAL_URL } from '@/const/url';
|
|
8
|
+
|
|
9
|
+
import ShareButton from '../../../../features/ShareButton';
|
|
10
|
+
import { useDetailContext } from '../../DetailProvider';
|
|
11
|
+
import AddGroupAgent from './AddGroupAgent';
|
|
12
|
+
|
|
13
|
+
const ActionButton = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
14
|
+
const { avatar, title, description, tags, identifier } = useDetailContext();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
18
|
+
<AddGroupAgent mobile={mobile} />
|
|
19
|
+
{identifier && (
|
|
20
|
+
<ShareButton
|
|
21
|
+
meta={{
|
|
22
|
+
avatar: avatar,
|
|
23
|
+
desc: description,
|
|
24
|
+
hashtags: tags,
|
|
25
|
+
title: title,
|
|
26
|
+
url: urlJoin(OFFICIAL_URL, '/community/group_agent', identifier),
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
)}
|
|
30
|
+
</Flexbox>
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export default ActionButton;
|
package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/Summary/index.tsx
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Collapse } from '@lobehub/ui';
|
|
2
|
+
import { cssVar } from 'antd-style';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import { useDetailContext } from '../../DetailProvider';
|
|
7
|
+
|
|
8
|
+
const Summary = memo(() => {
|
|
9
|
+
const { description, summary } = useDetailContext();
|
|
10
|
+
const { t } = useTranslation('discover');
|
|
11
|
+
|
|
12
|
+
const displayDescription = summary || description || 'No description provided';
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Collapse
|
|
16
|
+
defaultActiveKey={['summary']}
|
|
17
|
+
expandIconPlacement={'end'}
|
|
18
|
+
items={[
|
|
19
|
+
{
|
|
20
|
+
children: (
|
|
21
|
+
<p
|
|
22
|
+
style={{
|
|
23
|
+
color: cssVar.colorTextSecondary,
|
|
24
|
+
margin: 0,
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
{displayDescription}
|
|
28
|
+
</p>
|
|
29
|
+
),
|
|
30
|
+
key: 'summary',
|
|
31
|
+
label: t('groupAgents.details.summary.title', {
|
|
32
|
+
defaultValue: 'What can you use this group for?',
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
]}
|
|
36
|
+
size={'small'}
|
|
37
|
+
variant={'borderless'}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export default Summary;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Flexbox, ScrollShadow } from '@lobehub/ui';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
|
|
4
|
+
import { useQuery } from '@/hooks/useQuery';
|
|
5
|
+
|
|
6
|
+
import { GroupAgentNavKey } from '../Details/Nav';
|
|
7
|
+
import ActionButton from './ActionButton';
|
|
8
|
+
import Summary from './Summary';
|
|
9
|
+
|
|
10
|
+
const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
11
|
+
const { activeTab = GroupAgentNavKey.Overview } = useQuery() as { activeTab: GroupAgentNavKey };
|
|
12
|
+
|
|
13
|
+
if (mobile) {
|
|
14
|
+
return (
|
|
15
|
+
<Flexbox gap={32}>
|
|
16
|
+
<ActionButton mobile />
|
|
17
|
+
</Flexbox>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<ScrollShadow
|
|
23
|
+
flex={'none'}
|
|
24
|
+
gap={32}
|
|
25
|
+
hideScrollBar
|
|
26
|
+
size={4}
|
|
27
|
+
style={{
|
|
28
|
+
maxHeight: 'calc(100vh - 76px)',
|
|
29
|
+
paddingBottom: 24,
|
|
30
|
+
position: 'sticky',
|
|
31
|
+
top: 16,
|
|
32
|
+
}}
|
|
33
|
+
width={360}
|
|
34
|
+
>
|
|
35
|
+
<ActionButton />
|
|
36
|
+
{activeTab !== GroupAgentNavKey.Overview && <Summary />}
|
|
37
|
+
</ScrollShadow>
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export default Sidebar;
|
package/src/app/[variants]/(main)/community/(detail)/group_agent/features/StatusPage/index.tsx
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ExclamationCircleOutlined, FolderOpenOutlined } from '@ant-design/icons';
|
|
4
|
+
import { Button, FluentEmoji, Text } from '@lobehub/ui';
|
|
5
|
+
import { Result } from 'antd';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { useNavigate } from 'react-router-dom';
|
|
9
|
+
|
|
10
|
+
interface StatusPageProps {
|
|
11
|
+
status: 'unpublished' | 'archived' | 'deprecated';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const StatusPage = memo<StatusPageProps>(({ status }) => {
|
|
15
|
+
const navigate = useNavigate();
|
|
16
|
+
const { t } = useTranslation('discover');
|
|
17
|
+
|
|
18
|
+
const handleBackToMarket = () => {
|
|
19
|
+
navigate('/community');
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Unpublished status
|
|
23
|
+
if (status === 'unpublished') {
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
style={{
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
display: 'flex',
|
|
29
|
+
flex: 1,
|
|
30
|
+
justifyContent: 'center',
|
|
31
|
+
minHeight: '60vh',
|
|
32
|
+
padding: '20px',
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<Result
|
|
36
|
+
extra={
|
|
37
|
+
<Button onClick={handleBackToMarket} size={'large'} type="primary">
|
|
38
|
+
{t('groupAgents.status.backToMarket', { defaultValue: 'Back to Market' })}
|
|
39
|
+
</Button>
|
|
40
|
+
}
|
|
41
|
+
icon={<FluentEmoji emoji={'⌛'} size={96} type={'anim'} />}
|
|
42
|
+
subTitle={
|
|
43
|
+
<Text fontSize={16} type={'secondary'}>
|
|
44
|
+
{t('groupAgents.status.unpublished.subtitle', {
|
|
45
|
+
defaultValue:
|
|
46
|
+
'This group agent is under review. Please contact support@lobehub.com if you have questions.',
|
|
47
|
+
})}
|
|
48
|
+
</Text>
|
|
49
|
+
}
|
|
50
|
+
title={
|
|
51
|
+
<Text fontSize={28} weight={'bold'}>
|
|
52
|
+
{t('groupAgents.status.unpublished.title', { defaultValue: 'Under Review' })}
|
|
53
|
+
</Text>
|
|
54
|
+
}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Archived/Deprecated status
|
|
61
|
+
const isArchived = status === 'archived';
|
|
62
|
+
const statusKey = isArchived ? 'archived' : 'deprecated';
|
|
63
|
+
const statusIcon = isArchived ? (
|
|
64
|
+
<FolderOpenOutlined style={{ color: '#8c8c8c' }} />
|
|
65
|
+
) : (
|
|
66
|
+
<ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
style={{
|
|
72
|
+
alignItems: 'center',
|
|
73
|
+
display: 'flex',
|
|
74
|
+
flex: 1,
|
|
75
|
+
justifyContent: 'center',
|
|
76
|
+
minHeight: '60vh',
|
|
77
|
+
padding: '20px',
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<Result
|
|
81
|
+
extra={
|
|
82
|
+
<Button onClick={handleBackToMarket} type="primary">
|
|
83
|
+
{t('groupAgents.status.backToMarket', { defaultValue: 'Back to Market' })}
|
|
84
|
+
</Button>
|
|
85
|
+
}
|
|
86
|
+
icon={statusIcon}
|
|
87
|
+
subTitle={
|
|
88
|
+
<div style={{ color: '#666', lineHeight: 1.6 }}>
|
|
89
|
+
<p>
|
|
90
|
+
{t(`groupAgents.status.${statusKey}.subtitle`, {
|
|
91
|
+
defaultValue: `This group agent has been ${statusKey}.`,
|
|
92
|
+
})}
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
}
|
|
96
|
+
title={t(`groupAgents.status.${statusKey}.title`, {
|
|
97
|
+
defaultValue: isArchived ? 'Archived' : 'Deprecated',
|
|
98
|
+
})}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export default StatusPage;
|