mirror of
https://github.com/langgenius/dify.git
synced 2026-06-26 23:01:11 +08:00
feat(webapp): customize
This commit is contained in:
parent
238b9202d6
commit
f11b09abf1
@ -13,7 +13,8 @@ type IShareLinkProps = {
|
||||
onClose: () => void
|
||||
api_base_url: string
|
||||
appId: string
|
||||
mode: AppModeEnum
|
||||
mode?: AppModeEnum
|
||||
sourceCodeRepository?: 'webapp-conversation' | 'webapp-text-generator'
|
||||
}
|
||||
|
||||
const StepNum: FC<{ children: React.ReactNode }> = ({ children }) => (
|
||||
@ -38,10 +39,12 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||
appId,
|
||||
api_base_url,
|
||||
mode,
|
||||
sourceCodeRepository,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const isChatApp = mode === AppModeEnum.CHAT || mode === AppModeEnum.ADVANCED_CHAT
|
||||
const repository = sourceCodeRepository ?? (isChatApp ? 'webapp-conversation' : 'webapp-text-generator')
|
||||
const apiDocLink = docLink('/use-dify/publish/developing-with-apis')
|
||||
|
||||
return (
|
||||
@ -67,7 +70,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||
<div className="flex flex-col">
|
||||
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step1`, { ns: 'appOverview' })}</div>
|
||||
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step1Tip`, { ns: 'appOverview' })}</div>
|
||||
<Button nativeButton={false} render={<a href={`https://github.com/langgenius/${isChatApp ? 'webapp-conversation' : 'webapp-text-generator'}`} target="_blank" rel="noopener noreferrer" />}>
|
||||
<Button nativeButton={false} render={<a href={`https://github.com/langgenius/${repository}`} target="_blank" rel="noopener noreferrer" aria-label={t(`${prefixCustomize}.way1.step1Operation`, { ns: 'appOverview' })} />}>
|
||||
<GithubIcon className="mr-2 text-text-secondary" />
|
||||
{t(`${prefixCustomize}.way1.step1Operation`, { ns: 'appOverview' })}
|
||||
</Button>
|
||||
@ -78,7 +81,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||
<div className="flex flex-col">
|
||||
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step2`, { ns: 'appOverview' })}</div>
|
||||
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step2Tip`, { ns: 'appOverview' })}</div>
|
||||
<Button nativeButton={false} render={<a href="https://vercel.com/docs/concepts/deployments/git/vercel-for-github" target="_blank" rel="noopener noreferrer" />}>
|
||||
<Button nativeButton={false} render={<a href="https://vercel.com/docs/concepts/deployments/git/vercel-for-github" target="_blank" rel="noopener noreferrer" aria-label={t(`${prefixCustomize}.way1.step2Operation`, { ns: 'appOverview' })} />}>
|
||||
<div className="mr-1.5 border-t-0 border-r-[7px] border-b-12 border-l-[7px] border-solid border-text-primary border-t-transparent border-r-transparent border-l-transparent"></div>
|
||||
<span>{t(`${prefixCustomize}.way1.step2Operation`, { ns: 'appOverview' })}</span>
|
||||
</Button>
|
||||
@ -114,7 +117,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||
<p className="my-2 system-sm-medium text-text-secondary">{t(`${prefixCustomize}.way2.name`, { ns: 'appOverview' })}</p>
|
||||
<Button
|
||||
nativeButton={false}
|
||||
render={<a href={apiDocLink} target="_blank" rel="noopener noreferrer" />}
|
||||
render={<a href={apiDocLink} target="_blank" rel="noopener noreferrer" aria-label={t(`${prefixCustomize}.way2.operation`, { ns: 'appOverview' })} />}
|
||||
className="mt-2"
|
||||
>
|
||||
<span className="text-sm text-text-secondary">{t(`${prefixCustomize}.way2.operation`, { ns: 'appOverview' })}</span>
|
||||
|
||||
@ -115,6 +115,7 @@ function createAgent(overrides: Partial<AgentAppDetailWithSite> = {}): AgentAppD
|
||||
mode: 'agent',
|
||||
name: 'Support Agent',
|
||||
app_id: 'app-1',
|
||||
api_base_url: 'https://api.example.test/v1',
|
||||
access_mode: 'sso_verified',
|
||||
site: {
|
||||
access_token: 'site-token',
|
||||
@ -185,6 +186,29 @@ describe('Agent access surface cards', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should open the customize dialog with the backing app id and API base URL', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
renderWithQueryClient(
|
||||
<WebAppAccessCard agent={createAgent()} agentId="agent-1" isLoading={false} />,
|
||||
)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'agentV2.agentDetail.access.webApp.actions.customize' }))
|
||||
|
||||
const dialog = await screen.findByRole('dialog', { name: 'appOverview.overview.appInfo.customize.title' })
|
||||
expect(dialog).toHaveTextContent(/NEXT_PUBLIC_APP_ID=\s*'app-1'/)
|
||||
expect(dialog).toHaveTextContent(/NEXT_PUBLIC_API_URL=\s*'https:\/\/api\.example\.test\/v1'/)
|
||||
expect(within(dialog).getByRole('button', { name: /appOverview\.overview\.appInfo\.customize\.way1\.step1Operation/ })).toHaveAttribute('href', 'https://github.com/langgenius/webapp-conversation')
|
||||
})
|
||||
|
||||
it('should keep customize disabled until the generated contract provides the required fields', () => {
|
||||
renderWithQueryClient(
|
||||
<WebAppAccessCard agent={createAgent({ api_base_url: null })} agentId="agent-1" isLoading={false} />,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'agentV2.agentDetail.access.webApp.actions.customize' })).toBeDisabled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Service API access', () => {
|
||||
|
||||
@ -4,7 +4,9 @@ import type { AgentAppDetailWithSite } from '@dify/contracts/api/console/agent/t
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import CustomizeModal from '@/app/components/app/overview/customize'
|
||||
import ShareQRCode from '@/app/components/base/qrcode'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
@ -29,10 +31,18 @@ export function WebAppAccessCard({
|
||||
const { t: tCommon } = useTranslation('common')
|
||||
const queryClient = useQueryClient()
|
||||
const appId = agent?.app_id
|
||||
const apiBaseUrl = agent?.api_base_url
|
||||
const webAppUrl = getAgentWebAppUrl(agent)
|
||||
const isEnabled = Boolean(agent?.enable_site)
|
||||
const canManageWebApp = Boolean(appId)
|
||||
const customizeConfig = appId && apiBaseUrl
|
||||
? {
|
||||
apiBaseUrl,
|
||||
appId,
|
||||
}
|
||||
: null
|
||||
const showSsoBadge = agent?.access_mode === AccessMode.EXTERNAL_MEMBERS
|
||||
const [showCustomizeModal, setShowCustomizeModal] = useState(false)
|
||||
const toggleSiteMutation = useMutation(consoleQuery.apps.byAppId.siteEnable.post.mutationOptions({
|
||||
onSuccess: (_updatedApp, variables) => {
|
||||
queryClient.setQueryData<AgentAppDetailWithSite | undefined>(
|
||||
@ -156,7 +166,13 @@ export function WebAppAccessCard({
|
||||
<span aria-hidden className="i-ri-window-line size-4" />
|
||||
{t('agentDetail.access.webApp.actions.embedded')}
|
||||
</Button>
|
||||
<Button variant="secondary" size="medium" className="gap-1.5 px-3" disabled>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
className="gap-1.5 px-3"
|
||||
disabled={!customizeConfig}
|
||||
onClick={() => setShowCustomizeModal(true)}
|
||||
>
|
||||
<span aria-hidden className="i-ri-paint-brush-line size-4" />
|
||||
{t('agentDetail.access.webApp.actions.customize')}
|
||||
</Button>
|
||||
@ -164,6 +180,15 @@ export function WebAppAccessCard({
|
||||
<span aria-hidden className="i-ri-equalizer-2-line size-4" />
|
||||
{t('agentDetail.access.webApp.actions.settings')}
|
||||
</Button>
|
||||
{customizeConfig && (
|
||||
<CustomizeModal
|
||||
isShow={showCustomizeModal}
|
||||
onClose={() => setShowCustomizeModal(false)}
|
||||
appId={customizeConfig.appId}
|
||||
api_base_url={customizeConfig.apiBaseUrl}
|
||||
sourceCodeRepository="webapp-conversation"
|
||||
/>
|
||||
)}
|
||||
</AccessSurfaceCard>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user