diff --git a/web/app/components/header/account-dropdown/workplace-selector/index.tsx b/web/app/components/header/account-dropdown/workplace-selector/index.tsx
index 4d21ccdb59..fd3a997436 100644
--- a/web/app/components/header/account-dropdown/workplace-selector/index.tsx
+++ b/web/app/components/header/account-dropdown/workplace-selector/index.tsx
@@ -6,15 +6,12 @@ import { RiArrowDownSLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import { switchWorkspace } from '@/service/common'
import { useWorkspacesContext } from '@/context/workspace-context'
-import HeaderBillingBtn from '@/app/components/billing/header-billing-btn'
-import { useProviderContext } from '@/context/provider-context'
import { ToastContext } from '@/app/components/base/toast'
const WorkplaceSelector = () => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const { workspaces } = useWorkspacesContext()
- const { enableBilling } = useProviderContext()
const currentWorkspace = workspaces.find(v => v.current)
const handleSwitchWorkspace = async (tenant_id: string) => {
try {
@@ -69,11 +66,6 @@ const WorkplaceSelector = () => {
handleSwitchWorkspace(workspace.id)}>
{workspace.name[0].toLocaleUpperCase()}
{workspace.name}
- {enableBilling && (
-
-
-
- )}
))
}
diff --git a/web/app/components/plugins/install-plugin/hooks.ts b/web/app/components/plugins/install-plugin/hooks.ts
new file mode 100644
index 0000000000..2d4271e887
--- /dev/null
+++ b/web/app/components/plugins/install-plugin/hooks.ts
@@ -0,0 +1,68 @@
+import { useState } from 'react'
+import Toast from '@/app/components/base/toast'
+import { uploadGitHub } from '@/service/plugins'
+
+export const useGitHubReleases = () => {
+ const fetchReleases = async (owner: string, repo: string, setReleases: (releases: any) => void) => {
+ try {
+ const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases`)
+ if (!res.ok) throw new Error('Failed to fetch releases')
+ const data = await res.json()
+
+ const formattedReleases = data.map((release: any) => ({
+ tag_name: release.tag_name,
+ assets: release.assets.map((asset: any) => ({
+ browser_download_url: asset.browser_download_url,
+ name: asset.name,
+ })),
+ }))
+
+ setReleases(formattedReleases)
+ }
+ catch (error) {
+ Toast.notify({
+ type: 'error',
+ message: 'Failed to fetch repository releases',
+ })
+ }
+ }
+
+ return { fetchReleases }
+}
+
+export const useGitHubUpload = () => {
+ const [isLoading, setIsLoading] = useState(false)
+ const [error, setError] = useState(null)
+
+ const handleUpload = async (
+ repoUrl: string,
+ selectedVersion: string,
+ selectedPackage: string,
+ onSuccess?: (GitHubPackage: { manifest: any; uniqueIdentifier: string }) => void,
+ ) => {
+ setIsLoading(true)
+ setError(null)
+
+ try {
+ const response = await uploadGitHub(repoUrl, selectedVersion, selectedPackage)
+ const GitHubPackage = {
+ manifest: response.manifest,
+ uniqueIdentifier: response.plugin_unique_identifier,
+ }
+ if (onSuccess) onSuccess(GitHubPackage)
+ return GitHubPackage
+ }
+ catch (error) {
+ setError('Error installing package')
+ Toast.notify({
+ type: 'error',
+ message: 'Error installing package',
+ })
+ }
+ finally {
+ setIsLoading(false)
+ }
+ }
+
+ return { handleUpload, isLoading, error }
+}
diff --git a/web/app/components/plugins/install-plugin/install-from-github/index.tsx b/web/app/components/plugins/install-plugin/install-from-github/index.tsx
index 06c4c0797a..890424bfed 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/index.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/index.tsx
@@ -3,13 +3,16 @@
import React, { useState } from 'react'
import Modal from '@/app/components/base/modal'
import type { Item } from '@/app/components/base/select'
-import type { GitHubUrlInfo, InstallState } from '@/app/components/plugins/types'
+import type { InstallState } from '@/app/components/plugins/types'
+import { useGitHubReleases, useGitHubUpload } from '../hooks'
+import { parseGitHubUrl } from '../utils'
+import type { PluginDeclaration } from '../../types'
import { InstallStepFromGitHub } from '../../types'
import Toast from '@/app/components/base/toast'
import SetURL from './steps/setURL'
-import SetVersion from './steps/setVersion'
-import SetPackage from './steps/setPackage'
+import SelectPackage from './steps/selectPackage'
import Installed from './steps/installed'
+import Loaded from './steps/loaded'
import { useTranslation } from 'react-i18next'
type InstallFromGitHubProps = {
@@ -25,6 +28,8 @@ const InstallFromGitHub: React.FC = ({ onClose }) => {
selectedPackage: '',
releases: [],
})
+ const [uniqueIdentifier, setUniqueIdentifier] = useState(null)
+ const [manifest, setManifest] = useState(null)
const versions: Item[] = state.releases.map(release => ({
value: release.tag_name,
@@ -36,41 +41,16 @@ const InstallFromGitHub: React.FC = ({ onClose }) => {
.find(release => release.tag_name === state.selectedVersion)
?.assets
.map(asset => ({
- value: asset.browser_download_url,
+ value: asset.name,
name: asset.name,
})) || [])
: []
- const parseGitHubUrl = (url: string): GitHubUrlInfo => {
- const githubUrlRegex = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/
- const match = url.match(githubUrlRegex)
-
- if (match) {
- return {
- isValid: true,
- owner: match[1],
- repo: match[2],
- }
- }
-
- return { isValid: false }
- }
+ const { isLoading, handleUpload, error } = useGitHubUpload()
+ const { fetchReleases } = useGitHubReleases()
const handleInstall = async () => {
- // try {
- // const response = await installPackageFromGitHub({ repo: state.repoUrl, version: state.selectedVersion, package: state.selectedPackage })
- // if (response.plugin_unique_identifier) {
- // setState(prevState => ({...prevState, step: InstallStep.installed}))
- // console.log('Package installed:')
- // }
- // else {
- // console.error('Failed to install package:')
- // }
- // }
- // catch (error) {
- // console.error('Error installing package:')
- // }
- setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installed }))
+
}
const handleNext = async () => {
@@ -84,45 +64,48 @@ const InstallFromGitHub: React.FC = ({ onClose }) => {
})
break
}
- try {
- const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases`)
- if (!res.ok)
- throw new Error('Failed to fetch releases')
- const data = await res.json()
- const formattedReleases = data.map((release: any) => ({
- tag_name: release.tag_name,
- assets: release.assets.map((asset: any) => ({
- browser_download_url: asset.browser_download_url,
- id: asset.id,
- name: asset.name,
- })),
+ await fetchReleases(owner, repo, (fetchedReleases) => {
+ setState(prevState => ({
+ ...prevState,
+ releases: fetchedReleases,
+ step: InstallStepFromGitHub.selectPackage,
}))
- setState(prevState => ({ ...prevState, releases: formattedReleases, step: InstallStepFromGitHub.setVersion }))
- }
- catch (error) {
+ })
+ break
+ }
+ case InstallStepFromGitHub.selectPackage: {
+ const repo = state.repoUrl.replace('https://github.com/', '')
+ if (error) {
Toast.notify({
type: 'error',
- message: 'Failed to fetch repository release',
+ message: error,
+ })
+ }
+ else {
+ await handleUpload(repo, state.selectedVersion, state.selectedPackage, (GitHubPackage) => {
+ setManifest(GitHubPackage.manifest)
+ setUniqueIdentifier(GitHubPackage.uniqueIdentifier)
+ setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.loaded }))
})
}
break
}
- case InstallStepFromGitHub.setVersion:
- setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.setPackage }))
- break
- case InstallStepFromGitHub.setPackage:
+ case InstallStepFromGitHub.loaded:
+ setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installed }))
handleInstall()
break
+ case InstallStepFromGitHub.installed:
+ break
}
}
const handleBack = () => {
setState((prevState) => {
switch (prevState.step) {
- case InstallStepFromGitHub.setVersion:
+ case InstallStepFromGitHub.selectPackage:
return { ...prevState, step: InstallStepFromGitHub.setUrl }
- case InstallStepFromGitHub.setPackage:
- return { ...prevState, step: InstallStepFromGitHub.setVersion }
+ case InstallStepFromGitHub.loaded:
+ return { ...prevState, step: InstallStepFromGitHub.selectPackage }
default:
return prevState
}
@@ -155,22 +138,24 @@ const InstallFromGitHub: React.FC = ({ onClose }) => {
onCancel={onClose}
/>
)}
- {state.step === InstallStepFromGitHub.setVersion && (
- setState(prevState => ({ ...prevState, selectedVersion: item.value as string }))}
+ onSelectVersion={item => setState(prevState => ({ ...prevState, selectedVersion: item.value as string }))}
+ selectedPackage={state.selectedPackage}
+ packages={packages}
+ onSelectPackage={item => setState(prevState => ({ ...prevState, selectedPackage: item.value as string }))}
onNext={handleNext}
onBack={handleBack}
/>
)}
- {state.step === InstallStepFromGitHub.setPackage && (
- setState(prevState => ({ ...prevState, selectedPackage: item.value as string }))}
- onInstall={handleInstall}
+ {state.step === InstallStepFromGitHub.loaded && (
+
)}
{state.step === InstallStepFromGitHub.installed && (
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx
new file mode 100644
index 0000000000..42d847c584
--- /dev/null
+++ b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx
@@ -0,0 +1,55 @@
+'use client'
+
+import React from 'react'
+import Button from '@/app/components/base/button'
+import type { PluginDeclaration } from '../../../types'
+import Card from '../../../card'
+import Badge, { BadgeState } from '@/app/components/base/badge/index'
+import { pluginManifestToCardPluginProps } from '../../utils'
+import { useTranslation } from 'react-i18next'
+
+type LoadedProps = {
+ isLoading: boolean
+ payload: PluginDeclaration
+ onBack: () => void
+ onInstall: () => void
+}
+
+const i18nPrefix = 'plugin.installModal'
+
+const Loaded: React.FC = ({ isLoading, payload, onBack, onInstall }) => {
+ const { t } = useTranslation()
+ return (
+ <>
+
+
{t(`${i18nPrefix}.readyToInstall`)}
+
+
+ {payload.version}}
+ />
+
+
+
+
+
+ >
+ )
+}
+
+export default Loaded
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/setVersion.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx
similarity index 57%
rename from web/app/components/plugins/install-plugin/install-from-github/steps/setVersion.tsx
rename to web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx
index 042ab2b093..70ef1d8522 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/steps/setVersion.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx
@@ -1,18 +1,32 @@
+'use client'
+
import React from 'react'
import type { Item } from '@/app/components/base/select'
import { PortalSelect } from '@/app/components/base/select'
import Button from '@/app/components/base/button'
import { useTranslation } from 'react-i18next'
-type SetVersionProps = {
+type SelectPackageProps = {
selectedVersion: string
versions: Item[]
- onSelect: (item: Item) => void
+ onSelectVersion: (item: Item) => void
+ selectedPackage: string
+ packages: Item[]
+ onSelectPackage: (item: Item) => void
onNext: () => void
onBack: () => void
}
-const SetVersion: React.FC = ({ selectedVersion, versions, onSelect, onNext, onBack }) => {
+const SelectPackage: React.FC = ({
+ selectedVersion,
+ versions,
+ onSelectVersion,
+ selectedPackage,
+ packages,
+ onSelectPackage,
+ onNext,
+ onBack,
+}) => {
const { t } = useTranslation()
return (
<>
@@ -24,11 +38,24 @@ const SetVersion: React.FC = ({ selectedVersion, versions, onSe
+
+
@@ -50,4 +77,4 @@ const SetVersion: React.FC
= ({ selectedVersion, versions, onSe
)
}
-export default SetVersion
+export default SelectPackage
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/setPackage.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/setPackage.tsx
deleted file mode 100644
index 2db55aa56f..0000000000
--- a/web/app/components/plugins/install-plugin/install-from-github/steps/setPackage.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react'
-import type { Item } from '@/app/components/base/select'
-import { PortalSelect } from '@/app/components/base/select'
-import Button from '@/app/components/base/button'
-import { useTranslation } from 'react-i18next'
-
-type SetPackageProps = {
- selectedPackage: string
- packages: Item[]
- onSelect: (item: Item) => void
- onInstall: () => void
- onBack: () => void
-}
-
-const SetPackage: React.FC = ({ selectedPackage, packages, onSelect, onInstall, onBack }) => {
- const { t } = useTranslation()
- return (
- <>
-
-
-
-
-
-
- >
- )
-}
-
-export default SetPackage
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx
index 9ec6cd6eee..c6ce006f37 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx
@@ -1,3 +1,5 @@
+'use client'
+
import React from 'react'
import Button from '@/app/components/base/button'
import { useTranslation } from 'react-i18next'
diff --git a/web/app/components/plugins/install-plugin/utils.ts b/web/app/components/plugins/install-plugin/utils.ts
index 8b3e850deb..7677bf5b73 100644
--- a/web/app/components/plugins/install-plugin/utils.ts
+++ b/web/app/components/plugins/install-plugin/utils.ts
@@ -1,4 +1,5 @@
import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../types'
+import type { GitHubUrlInfo } from '@/app/components/plugins/types'
export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaration): Plugin => {
return {
@@ -18,6 +19,7 @@ export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaratio
endpoint: {
settings: [],
},
+ tags: [],
}
}
@@ -39,5 +41,11 @@ export const pluginManifestInMarketToPluginProps = (pluginManifest: PluginManife
endpoint: {
settings: [],
},
+ tags: [],
}
}
+
+export const parseGitHubUrl = (url: string): GitHubUrlInfo => {
+ const match = url.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/)
+ return match ? { isValid: true, owner: match[1], repo: match[2] } : { isValid: false }
+}
diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts
index ae16b0b300..f3cfd7f93c 100644
--- a/web/app/components/plugins/types.ts
+++ b/web/app/components/plugins/types.ts
@@ -133,8 +133,8 @@ export type Permissions = {
export enum InstallStepFromGitHub {
setUrl = 'url',
- setVersion = 'version',
- setPackage = 'package',
+ selectPackage = 'selecting',
+ loaded = 'loaded',
installed = 'installed',
}
@@ -205,6 +205,11 @@ export type InstallPackageResponse = {
task_id: string
}
+export type uploadGitHubResponse = {
+ plugin_unique_identifier: string
+ manifest: PluginDeclaration
+}
+
export type DebugInfo = {
key: string
host: string
diff --git a/web/service/plugins.ts b/web/service/plugins.ts
index 43a57a24b4..e122b8efea 100644
--- a/web/service/plugins.ts
+++ b/web/service/plugins.ts
@@ -14,6 +14,7 @@ import type {
TaskStatusResponse,
UninstallPluginResponse,
UpdateEndpointRequest,
+ uploadGitHubResponse,
} from '@/app/components/plugins/types'
import type { DebugInfo as DebugInfoTypes } from '@/app/components/plugins/types'
import type {
@@ -51,12 +52,6 @@ export const disableEndpoint: Fetcher(url, { body: { endpoint_id: endpointID } })
}
-export const installPackageFromGitHub: Fetcher = ({ repo, version, package: packageName }) => {
- return post('/workspaces/current/plugin/upload/github', {
- body: { repo, version, package: packageName },
- })
-}
-
export const fetchDebugKey = async () => {
return get('/workspaces/current/plugin/debugging-key')
}
@@ -76,6 +71,22 @@ export const installPackageFromLocal = async (uniqueIdentifier: string) => {
})
}
+export const uploadGitHub = async (repoUrl: string, selectedVersion: string, selectedPackage: string) => {
+ return post('/workspaces/current/plugin/upload/github', {
+ body: {
+ repo: repoUrl,
+ version: selectedVersion,
+ package: selectedPackage,
+ },
+ })
+}
+
+export const installPackageFromGitHub = async (uniqueIdentifier: string) => {
+ return post('/workspaces/current/plugin/install/github', {
+ body: { plugin_unique_identifiers: [uniqueIdentifier] },
+ })
+}
+
export const fetchIcon = (tenantId: string, fileName: string) => {
return get(`workspaces/current/plugin/icon?tenant_id=${tenantId}&filename=${fileName}`)
}