diff --git a/web/app/components/explore/item-operation/index.tsx b/web/app/components/explore/item-operation/index.tsx index 53e82595c8..00b8f3cbc9 100644 --- a/web/app/components/explore/item-operation/index.tsx +++ b/web/app/components/explore/item-operation/index.tsx @@ -1,30 +1,52 @@ 'use client' import React, { FC } from 'react' import cn from 'classnames' +import { useTranslation } from 'react-i18next' import Popover from '@/app/components/base/popover' import { TrashIcon } from '@heroicons/react/24/outline' import s from './style.module.css' +const PinIcon = ( + + + +) + export interface IItemOperationProps { className?: string + isPinned: boolean + isShowDelete: boolean + togglePin: () => void onDelete: () => void } const ItemOperation: FC = ({ className, + isPinned, + isShowDelete, + togglePin, onDelete }) => { + const { t } = useTranslation() + return ( { e.stopPropagation() }}> -
- - {'Delete'} +
+ {PinIcon} + {isPinned ? t('explore.sideBar.action.unpin') : t('explore.sideBar.action.pin')}
+ {isShowDelete && ( +
+ + {t('explore.sideBar.action.delete')} +
+ )} +
} trigger='click' diff --git a/web/app/components/explore/sidebar/app-nav-item/index.tsx b/web/app/components/explore/sidebar/app-nav-item/index.tsx index 9b9ccc53fe..d1173f8b28 100644 --- a/web/app/components/explore/sidebar/app-nav-item/index.tsx +++ b/web/app/components/explore/sidebar/app-nav-item/index.tsx @@ -5,17 +5,25 @@ import ItemOperation from '@/app/components/explore/item-operation' import s from './style.module.css' -export default function NavLink({ - name, - id, - isSelected, - onDelete -}: { +export interface IAppNavItemProps { name: string id: string isSelected: boolean + isPinned: boolean + togglePin: () => void + uninstallable: boolean onDelete: (id: string) => void -}) { +} + +export default function AppNavItem({ + name, + id, + isSelected, + isPinned, + togglePin, + uninstallable, + onDelete +}: IAppNavItemProps) { const router = useRouter() const url = `/explore/installed/${id}` @@ -46,7 +54,9 @@ export default function NavLink({ !isSelected && (
e.stopPropagation()}> onDelete(id)} />
diff --git a/web/app/components/explore/sidebar/index.tsx b/web/app/components/explore/sidebar/index.tsx index 7df78cb60d..c68e1b57c3 100644 --- a/web/app/components/explore/sidebar/index.tsx +++ b/web/app/components/explore/sidebar/index.tsx @@ -7,7 +7,8 @@ import cn from 'classnames' import { useSelectedLayoutSegments } from 'next/navigation' import Link from 'next/link' import Item from './app-nav-item' -import { fetchInstalledAppList as doFetchInstalledAppList, uninstallApp } from '@/service/explore' +import { fetchInstalledAppList as doFetchInstalledAppList, uninstallApp, updatePinStatus } from '@/service/explore' +import Toast from '../../base/toast' const SelectedDiscoveryIcon = () => ( @@ -39,6 +40,19 @@ const SideBar: FC<{ const handleDelete = async (id: string) => { await uninstallApp(id) + Toast.notify({ + type: 'success', + message: t('common.api.Removed') + }) + fetchInstalledAppList() + } + + const handleUpdatePinStatus = async (id: string, isPinned: boolean) => { + await updatePinStatus(id, isPinned) + Toast.notify({ + type: 'success', + message: t('common.api.success') + }) fetchInstalledAppList() } @@ -66,13 +80,16 @@ const SideBar: FC<{
{t('explore.sidebar.workspace')}
- {installedApps.map(({id, app : { name }}) => { + {installedApps.map(({id, is_pinned, uninstallable, app : { name }}) => { return ( handleUpdatePinStatus(id, !is_pinned)} + uninstallable={uninstallable} onDelete={handleDelete} /> ) diff --git a/web/i18n/lang/explore.en.ts b/web/i18n/lang/explore.en.ts index 06058dd7ff..699e1725cb 100644 --- a/web/i18n/lang/explore.en.ts +++ b/web/i18n/lang/explore.en.ts @@ -16,6 +16,13 @@ const translation = { title: 'Create app from {{name}}', subTitle: 'App icon & name', nameRequired: 'App name is required', + }, + sideBar: { + action: { + pin: 'Pin', + unpin: 'Unpin', + delete: 'Delete', + } } } diff --git a/web/i18n/lang/explore.zh.ts b/web/i18n/lang/explore.zh.ts index 66b65bdfff..f8fcee793f 100644 --- a/web/i18n/lang/explore.zh.ts +++ b/web/i18n/lang/explore.zh.ts @@ -16,6 +16,13 @@ const translation = { title: '从 {{name}} 创建应用程序', subTitle: '应用程序图标和名称', nameRequired: '应用程序名称不能为空', + }, + sideBar: { + action: { + pin: '置顶', + unpin: '取消置顶', + delete: '删除', + } } } diff --git a/web/models/explore.ts b/web/models/explore.ts index 65f2e175fb..c950df3e31 100644 --- a/web/models/explore.ts +++ b/web/models/explore.ts @@ -26,4 +26,5 @@ export type InstalledApp = { app: AppBasicInfo; id: string; uninstallable: boolean + is_pinned: boolean } \ No newline at end of file diff --git a/web/service/explore.ts b/web/service/explore.ts index 64a46381ca..2d6e69c264 100644 --- a/web/service/explore.ts +++ b/web/service/explore.ts @@ -1,4 +1,4 @@ -import { get, post, del } from './base' +import { get, post, del, patch } from './base' export const fetchAppList = () => { return get('/explore/apps') @@ -23,3 +23,11 @@ export const installApp = (id: string) => { export const uninstallApp = (id: string) => { return del(`/installed-apps/${id}`) } + +export const updatePinStatus = (id: string, isPinned: boolean) => { + return patch(`/installed-apps/${id}`, { + body: { + is_pinned: isPinned + } + }) +}