Skip to content

Commit a0a259a

Browse files
FinleyGec121914yudependabot[bot]heheer
committed
feat: marketplace batch update (#6299)
* feat: wecom integration (#6234) * wip: wecom * feat: template filter by user tag * feat: wecom pay * fix: some bug * fix: zod error * feat: bill * fix: bill toast * feat: forbidden downgrade plan button * chore(fe): wecom zone * chore: use queue to delete team instead of a session * chore: adjust * feat: wecom bill logic refactor * perf: plan * perf: plan * fix: i18n * chore: adjust * feat: adjust * feat: add wecom config * perf: wecom app template recommendation & system tool preinstall * perf: some variable and tip * update create bill api schema * update create bill api schema * update create bill api schema * feat: wecom * chore: adjust * fix: ts * perf: condition order * perf: condition order --------- Co-authored-by: archer <545436317@qq.com> * feat: system tool config tags (#6257) * fix: system secret (#6259) * fix: system secret * chore: update docs * chore: merge main (#6264) * feat: wecom integration (#6234) * wip: wecom * feat: template filter by user tag * feat: wecom pay * fix: some bug * fix: zod error * feat: bill * fix: bill toast * feat: forbidden downgrade plan button * chore(fe): wecom zone * chore: use queue to delete team instead of a session * chore: adjust * feat: wecom bill logic refactor * perf: plan * perf: plan * fix: i18n * chore: adjust * feat: adjust * feat: add wecom config * perf: wecom app template recommendation & system tool preinstall * perf: some variable and tip * update create bill api schema * update create bill api schema * update create bill api schema * feat: wecom * chore: adjust * fix: ts * perf: condition order * perf: condition order --------- Co-authored-by: archer <545436317@qq.com> * feat: system tool config tags (#6257) * fix: system secret (#6259) * fix: system secret * chore: update docs --------- Co-authored-by: archer <545436317@qq.com> * fix: custom domain limitation (#6265) * chore: update version number (#6266) * fix: price status (#6279) * fix: back button (#6281) * chore/rebase main (#6295) * chore(deps): bump undici from 7.16.0 to 7.18.2 (#6272) Bumps [undici](https://github.com/nodejs/undici) from 7.16.0 to 7.18.2. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](nodejs/undici@v7.16.0...v7.18.2) --- updated-dependencies: - dependency-name: undici dependency-version: 7.18.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump undici in /plugins/webcrawler/SPIDER (#6273) Bumps [undici](https://github.com/nodejs/undici) from 6.21.3 to 6.23.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](nodejs/undici@v6.21.3...v6.23.0) --- updated-dependencies: - dependency-name: undici dependency-version: 6.23.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump tar from 7.4.3 to 7.5.3 in /document (#6282) Bumps [tar](https://github.com/isaacs/node-tar) from 7.4.3 to 7.5.3. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](isaacs/node-tar@v7.4.3...v7.5.3) --- updated-dependencies: - dependency-name: tar dependency-version: 7.5.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * perf: remove request invalid field (#6283) * V4.14.5.1 dev (#6290) * chore: cherry pick some commits from v4.14.6-dev (#6287) * fix: custom domain limitation (#6265) * fix: system secret (#6259) * fix: system secret * chore: update docs * chore: docs * fix password variable & datetime picker (#6276) * fix password variable & datetime picker * doc * chore: cherry pick some commits from v4.14.6-dev (#6287) * fix: custom domain limitation (#6265) * fix: system secret (#6259) * fix: system secret * chore: update docs * chore: docs * doc * chore: docs --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: Finley Ge <finleyge@fastgpt.io> * perf: extname computed (#6285) * perf: extname computed * chore: handle hash or query flags --------- Co-authored-by: Finley Ge <finleyge@fastgpt.io> * chore: docs (#6291) --------- Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Archer <545436317@qq.com> * chore: deploy scripts (#6293) * docs: 41451 upgrade doc (#6294) * feat: wecom integration (#6234) * wip: wecom * feat: template filter by user tag * feat: wecom pay * fix: some bug * fix: zod error * feat: bill * fix: bill toast * feat: forbidden downgrade plan button * chore(fe): wecom zone * chore: use queue to delete team instead of a session * chore: adjust * feat: wecom bill logic refactor * perf: plan * perf: plan * fix: i18n * chore: adjust * feat: adjust * feat: add wecom config * perf: wecom app template recommendation & system tool preinstall * perf: some variable and tip * update create bill api schema * update create bill api schema * update create bill api schema * feat: wecom * chore: adjust * fix: ts * perf: condition order * perf: condition order --------- Co-authored-by: archer <545436317@qq.com> * feat: system tool config tags (#6257) * fix: price status (#6279) * fix: back button (#6281) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Archer <545436317@qq.com> Co-authored-by: heheer <heheer@sealos.io> * feat: marketplace batch update * chore: components reuse * fix: test case --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: archer <545436317@qq.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io>
1 parent a2ba05f commit a0a259a

20 files changed

Lines changed: 850 additions & 254 deletions

File tree

packages/global/openapi/core/plugin/marketplace/api.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,12 @@ export const GetSystemInstalledPluginsResponseSchema = z.object({
5757
list: z.array(
5858
z.object({
5959
id: z.string(),
60-
version: z.string()
60+
version: z.string(),
61+
name: z.any().optional(),
62+
description: z.any().optional(),
63+
icon: z.string().optional(),
64+
author: z.string().optional(),
65+
tags: z.array(z.string()).optional()
6166
})
6267
)
6368
});
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
import React, { useState, useCallback, useEffect } from 'react';
2+
import {
3+
Box,
4+
Button,
5+
Checkbox,
6+
Drawer,
7+
DrawerBody,
8+
DrawerContent,
9+
DrawerHeader,
10+
DrawerOverlay,
11+
Flex,
12+
VStack,
13+
Accordion
14+
} from '@chakra-ui/react';
15+
import { useTranslation } from 'next-i18next';
16+
import Avatar from '../../../common/Avatar';
17+
import { parseI18nString } from '@fastgpt/global/common/i18n/utils';
18+
import MyIconButton from '../../../common/Icon/button';
19+
import LightRowTabs from '../../../common/Tabs/LightRowTabs';
20+
import { type ToolCardItemType } from './ToolCard';
21+
import MyBox from '../../../common/MyBox';
22+
import Markdown from '../../../common/Markdown';
23+
import type { GetTeamToolDetailResponseType } from '@fastgpt/global/openapi/core/plugin/team/toolApi';
24+
import { useTableMultipleSelect } from '../../../../hooks/useTableMultipleSelect';
25+
import {
26+
ParamSection,
27+
SubToolAccordionItem,
28+
useToolDetail,
29+
drawerScrollbarStyles
30+
} from './ToolDetail';
31+
32+
type ViewMode = 'list' | 'detail';
33+
34+
interface BatchUpdateDrawerProps {
35+
isOpen: boolean;
36+
onClose: () => void;
37+
updatableTools: ToolCardItemType[];
38+
onBatchUpdate: (toolIds: string[]) => Promise<void>;
39+
isBatchUpdating: boolean;
40+
onFetchDetail?: (toolId: string) => Promise<GetTeamToolDetailResponseType>;
41+
}
42+
43+
const BatchUpdateDrawer: React.FC<BatchUpdateDrawerProps> = ({
44+
isOpen,
45+
onClose,
46+
updatableTools,
47+
onBatchUpdate,
48+
isBatchUpdating,
49+
onFetchDetail
50+
}) => {
51+
const { t, i18n } = useTranslation();
52+
const [viewMode, setViewMode] = useState<ViewMode>('list');
53+
const [selectedToolForDetail, setSelectedToolForDetail] = useState<ToolCardItemType | null>(null);
54+
const [activeTab, setActiveTab] = useState<'guide' | 'params'>('params');
55+
const [isUpdatingSingle, setIsUpdatingSingle] = useState(false);
56+
57+
// Use table multiple select hook
58+
const {
59+
selectedItems,
60+
isSelecteAll,
61+
selectAllTrigger,
62+
hasSelections,
63+
toggleSelect,
64+
isSelected,
65+
FloatingActionBar,
66+
setSelectedItems
67+
} = useTableMultipleSelect<ToolCardItemType>({
68+
list: updatableTools,
69+
getItemId: (tool: ToolCardItemType) => tool.id
70+
});
71+
72+
// Use tool detail hook
73+
const { parentTool, isToolSet, subTools, readmeContent, loadingDetail } = useToolDetail({
74+
toolId: selectedToolForDetail?.id,
75+
tags: selectedToolForDetail?.tags || undefined,
76+
onFetchDetail,
77+
autoFetch: viewMode === 'detail'
78+
});
79+
80+
// Reset view mode when drawer closes
81+
useEffect(() => {
82+
if (!isOpen) {
83+
setViewMode('list');
84+
setSelectedToolForDetail(null);
85+
setActiveTab('params');
86+
setSelectedItems([]);
87+
}
88+
}, [isOpen, setSelectedItems]);
89+
90+
const handleViewDetail = useCallback((tool: ToolCardItemType) => {
91+
setSelectedToolForDetail(tool);
92+
setViewMode('detail');
93+
}, []);
94+
95+
const handleBack = useCallback(() => {
96+
setViewMode('list');
97+
setSelectedToolForDetail(null);
98+
setActiveTab('params');
99+
}, []);
100+
101+
const handleUpdateSingle = useCallback(async () => {
102+
if (!selectedToolForDetail) return;
103+
104+
setIsUpdatingSingle(true);
105+
try {
106+
await onBatchUpdate([selectedToolForDetail.id]);
107+
// Go back to list view after successful update
108+
handleBack();
109+
} finally {
110+
setIsUpdatingSingle(false);
111+
}
112+
}, [selectedToolForDetail, onBatchUpdate, handleBack]);
113+
114+
return (
115+
<Drawer isOpen={isOpen} onClose={onClose} placement="right">
116+
<DrawerOverlay />
117+
<DrawerContent maxW="480px">
118+
<DrawerHeader pt={6} pb={1}>
119+
{viewMode === 'list' ? (
120+
<Flex gap={1.5} alignItems="center">
121+
<Box fontSize={'16px'} fontWeight={500} color={'myGray.900'}>
122+
{t('app:toolkit_updatable_plugins')}
123+
</Box>
124+
<Box flex={1} />
125+
<MyIconButton icon={'common/closeLight'} onClick={onClose} />
126+
</Flex>
127+
) : (
128+
<Flex gap={1.5}>
129+
<Avatar src={parentTool?.icon || ''} borderRadius={'md'} w={6} />
130+
<Box fontSize={'16px'} fontWeight={500} color={'myGray.900'}>
131+
{parseI18nString(parentTool?.name || '', i18n.language)}
132+
</Box>
133+
<Box flex={1} />
134+
<MyIconButton icon={'common/backFill'} onClick={handleBack} />
135+
</Flex>
136+
)}
137+
</DrawerHeader>
138+
139+
<DrawerBody position="relative" sx={drawerScrollbarStyles}>
140+
{viewMode === 'list' ? (
141+
<VStack align="stretch" spacing={0} pb={20}>
142+
{updatableTools.map((tool) => (
143+
<Flex
144+
key={tool.id}
145+
align="center"
146+
p={3}
147+
borderRadius="md"
148+
borderBottom={'1px solid'}
149+
borderColor={'myGray.200'}
150+
_hover={{ bg: 'myGray.50' }}
151+
cursor="pointer"
152+
onClick={() => toggleSelect(tool)}
153+
>
154+
<Flex onClick={(e) => e.stopPropagation()} mr={3} align="center">
155+
<Checkbox isChecked={isSelected(tool)} onChange={() => toggleSelect(tool)} />
156+
</Flex>
157+
<Avatar src={tool.icon} w={6} h={6} borderRadius="md" mr={3} flexShrink={0} />
158+
<Box flex={1}>
159+
<Box fontSize="sm" fontWeight="medium" color="myGray.900">
160+
{parseI18nString(tool.name, i18n.language)}
161+
</Box>
162+
</Box>
163+
<Box
164+
as="button"
165+
fontSize="sm"
166+
color="primary.600"
167+
_hover={{ textDecoration: 'underline' }}
168+
onClick={(e: any) => {
169+
e.stopPropagation();
170+
handleViewDetail(tool);
171+
}}
172+
>
173+
{t('common:view_detail')}
174+
</Box>
175+
</Flex>
176+
))}
177+
178+
{/* Bottom action bar */}
179+
<FloatingActionBar
180+
position="fixed"
181+
bottom={0}
182+
left={0}
183+
right={0}
184+
maxW="480px"
185+
py={4}
186+
activeBg="white"
187+
activedStyles={{
188+
boxShadow: '0 -2px 8px rgba(0, 0, 0, 0.08)',
189+
gap: 3
190+
}}
191+
Controler={
192+
<Button
193+
flex={1}
194+
variant="primary"
195+
isLoading={isBatchUpdating}
196+
onClick={() =>
197+
onBatchUpdate(selectedItems.map((tool: ToolCardItemType) => tool.id))
198+
}
199+
>
200+
{t('app:toolkit_batch_update')}
201+
</Button>
202+
}
203+
/>
204+
</VStack>
205+
) : (
206+
<MyBox>
207+
<Flex gap={2} flexWrap="wrap">
208+
{parentTool?.tags?.map((tag: string) => (
209+
<Box
210+
key={tag}
211+
px={2}
212+
py={1}
213+
border={'1px solid'}
214+
borderRadius={'6px'}
215+
borderColor={'myGray.200'}
216+
fontSize={'10px'}
217+
fontWeight={'medium'}
218+
color={'myGray.700'}
219+
>
220+
{tag}
221+
</Box>
222+
))}
223+
</Flex>
224+
<Box fontSize={'12px'} color="myGray.500" mt={3}>
225+
{parseI18nString(parentTool?.description || '', i18n.language)}
226+
</Box>
227+
<Box fontSize={'12px'} color="myGray.500" mt={3}>
228+
{`by ${parentTool?.author || 'FastGPT'}`}
229+
</Box>
230+
<Flex mt={3} gap={2}>
231+
<Button
232+
flex={1}
233+
variant="primary"
234+
isLoading={isUpdatingSingle || loadingDetail}
235+
onClick={handleUpdateSingle}
236+
>
237+
{t('app:custom_plugin_update')}
238+
</Button>
239+
</Flex>
240+
241+
<Flex mt={4} gap={1.5} alignItems={'center'}>
242+
<Box fontWeight={'medium'} fontSize={'14px'} color={'myGray.900'}>
243+
{t('app:toolkit_activation_label')}
244+
</Box>
245+
<Box fontSize={'12px'} color={'myGray.600'}>
246+
{parentTool?.hasSystemSecret ||
247+
(parentTool?.secretInputConfig && parentTool?.secretInputConfig.length > 0) ||
248+
(parentTool?.inputList && parentTool?.inputList.length > 0)
249+
? t('app:toolkit_activation_required')
250+
: t('app:toolkit_activation_not_required')}
251+
</Box>
252+
</Flex>
253+
254+
<Box mt={4}>
255+
<LightRowTabs
256+
list={[
257+
{
258+
label: isToolSet
259+
? t('app:toolkit_tool_list')
260+
: t('app:toolkit_params_description'),
261+
value: 'params'
262+
},
263+
...(parentTool?.courseUrl || parentTool?.readme || parentTool?.userGuide
264+
? [{ label: t('app:toolkit_user_guide'), value: 'guide' }]
265+
: [])
266+
]}
267+
value={activeTab}
268+
onChange={(value) => {
269+
if (value === 'guide' && parentTool?.courseUrl) {
270+
window.open(parentTool?.courseUrl, '_blank');
271+
} else {
272+
setActiveTab(value as 'guide' | 'params');
273+
}
274+
}}
275+
gap={4}
276+
/>
277+
<Box h={'1px'} w={'full'} bg={'myGray.200'} mt={'-5px'} mx={1} />
278+
</Box>
279+
280+
<Box mt={4}>
281+
{activeTab === 'guide' && (
282+
<VStack align="stretch" spacing={4}>
283+
{(readmeContent || parentTool?.userGuide) && (
284+
<Box
285+
px={4}
286+
py={3}
287+
border="1px solid"
288+
borderColor="myGray.200"
289+
borderRadius="md"
290+
bg="myGray.50"
291+
fontSize="sm"
292+
color="myGray.900"
293+
maxH="400px"
294+
overflowY="auto"
295+
>
296+
<Markdown source={readmeContent || parentTool?.userGuide || ''} />
297+
</Box>
298+
)}
299+
</VStack>
300+
)}
301+
302+
{activeTab === 'params' && (
303+
<VStack align="stretch" spacing={4}>
304+
{isToolSet && subTools.length > 0 && (
305+
<Accordion
306+
allowMultiple
307+
{...(subTools.length === 1 ? { defaultIndex: [0] } : {})}
308+
>
309+
{subTools.map((subTool) => (
310+
<SubToolAccordionItem key={subTool.toolId} tool={subTool} />
311+
))}
312+
</Accordion>
313+
)}
314+
315+
{!isToolSet && (
316+
<>
317+
{parentTool?.versionList?.[0]?.inputs &&
318+
parentTool?.versionList?.[0]?.inputs.length > 0 && (
319+
<ParamSection
320+
title={t('app:toolkit_inputs')}
321+
params={parentTool?.versionList?.[0]?.inputs}
322+
/>
323+
)}
324+
{parentTool?.versionList?.[0]?.outputs &&
325+
parentTool?.versionList?.[0]?.outputs.length > 0 && (
326+
<ParamSection
327+
title={t('app:toolkit_outputs')}
328+
params={parentTool?.versionList?.[0]?.outputs}
329+
/>
330+
)}
331+
</>
332+
)}
333+
</VStack>
334+
)}
335+
</Box>
336+
</MyBox>
337+
)}
338+
</DrawerBody>
339+
</DrawerContent>
340+
</Drawer>
341+
);
342+
};
343+
344+
export default React.memo(BatchUpdateDrawer);

0 commit comments

Comments
 (0)