fix(agent-v2): refine access card interactions (#41254)

This commit is contained in:
yyh
2026-08-25 12:25:49 +00:00
committed by GitHub
parent ed184a6632
commit db5121828e
5 changed files with 307 additions and 112 deletions
@@ -1,5 +1,10 @@
import type { AgentAppDetailWithSite } from '@dify/contracts/api/console/agent/types.gen'
import type {
AgentApiAccessResponse,
AgentAppDetailWithSite,
} from '@dify/contracts/api/console/agent/types.gen'
import type { AppDetail } from '@dify/contracts/api/console/apps/types.gen'
import type React from 'react'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
@@ -206,6 +211,42 @@ function createAgent(overrides: Partial<AgentAppDetailWithSite> = {}): AgentAppD
}
}
function createAppDetailResponse(overrides: Partial<AppDetail> = {}): AppDetail {
return {
enable_api: true,
enable_site: true,
id: 'app-1',
mode: 'agent',
name: 'Support Agent',
...overrides,
}
}
function createAgentApiAccessResponse(
overrides: Partial<AgentApiAccessResponse> = {},
): AgentApiAccessResponse {
const serviceApiBaseUrl = 'https://api.example.test/v1'
return {
access_ready: true,
api_key_count: 2,
api_rph: 0,
api_rpm: 0,
chat_endpoint: `${serviceApiBaseUrl}/chat-messages`,
conversations_endpoint: `${serviceApiBaseUrl}/conversations`,
enabled: true,
files_upload_endpoint: `${serviceApiBaseUrl}/files/upload`,
info_endpoint: `${serviceApiBaseUrl}/info`,
messages_endpoint: `${serviceApiBaseUrl}/messages`,
meta_endpoint: `${serviceApiBaseUrl}/meta`,
parameters_endpoint: `${serviceApiBaseUrl}/parameters`,
service_api_base_url: serviceApiBaseUrl,
stop_endpoint: `${serviceApiBaseUrl}/chat-messages/{task_id}/stop`,
streaming_only: true,
...overrides,
}
}
function renderWithQueryClient(
ui: React.ReactElement,
{ webAppAuthEnabled = true }: { webAppAuthEnabled?: boolean } = {},
@@ -237,19 +278,36 @@ function createConsoleQueryClient(webAppAuthEnabled = true) {
return queryClient
}
function createDeferredPromise<T>() {
let resolve!: (value: T) => void
let reject!: (reason?: unknown) => void
const promise = new Promise<T>((promiseResolve, promiseReject) => {
resolve = promiseResolve
reject = promiseReject
})
return { promise, reject, resolve }
}
describe('Agent access surface cards', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('Web app access', () => {
it('should render the backend web app URL and toggle site status through the backing app id', async () => {
it('should serialize Web App toggles and cache each confirmed response', async () => {
const user = userEvent.setup()
mocks.siteEnableMutation.mockResolvedValueOnce({ enable_site: false })
const firstToggle = createDeferredPromise<AppDetail>()
const secondToggle = createDeferredPromise<AppDetail>()
mocks.siteEnableMutation
.mockReturnValueOnce(firstToggle.promise)
.mockReturnValueOnce(secondToggle.promise)
renderWithQueryClient(
<WebAppAccessCard agent={createAgent()} agentId="agent-1" isLoading={false} />,
const agent = createAgent()
const queryClient = renderWithQueryClient(
<WebAppAccessCard agent={agent} agentId="agent-1" isLoading={false} />,
)
queryClient.setQueryData(['agent-detail', 'agent-1'], agent)
expect(screen.getByText('https://chat.example.test/agent/site-token')).toBeInTheDocument()
expect(
@@ -257,22 +315,115 @@ describe('Agent access surface cards', () => {
).toHaveAttribute('href', 'https://chat.example.test/agent/site-token')
expect(screen.getByText('agentV2.agentDetail.access.webApp.ssoEnabled')).toBeInTheDocument()
await user.click(
screen.getByRole('switch', {
name: 'agentV2.agentDetail.access.toggleSurface:{"name":"agentV2.agentDetail.access.webApp.title"}',
const accessSwitch = screen.getByRole('switch', {
name: 'agentV2.agentDetail.access.toggleSurface:{"name":"agentV2.agentDetail.access.webApp.title"}',
})
await user.click(accessSwitch)
expect(accessSwitch).toHaveAttribute('aria-checked', 'false')
expect(accessSwitch).toBeEnabled()
expect(
screen.getByRole('button', { name: 'agentV2.agentDetail.access.webApp.actions.launch' }),
).toBeDisabled()
expect(mocks.siteEnableMutation.mock.calls[0]?.[0]).toEqual({
params: {
app_id: 'app-1',
},
body: {
enable_site: false,
},
})
await user.click(accessSwitch)
expect(accessSwitch).toHaveAttribute('aria-checked', 'true')
expect(
screen.getByRole('button', { name: 'agentV2.agentDetail.access.webApp.actions.launch' }),
).toBeDisabled()
expect(mocks.siteEnableMutation).toHaveBeenCalledTimes(1)
firstToggle.resolve(
createAppDetailResponse({
enable_site: false,
updated_at: 1781660200,
updated_by: 'user-2',
}),
)
await waitFor(() => {
expect(mocks.siteEnableMutation.mock.calls[0]?.[0]).toEqual({
expect(mocks.siteEnableMutation.mock.calls[1]?.[0]).toEqual({
params: {
app_id: 'app-1',
},
body: {
enable_site: false,
enable_site: true,
},
})
})
expect(queryClient.getQueryData(['agent-detail', 'agent-1'])).toMatchObject({
enable_site: false,
updated_at: 1781660200,
updated_by: 'user-2',
})
secondToggle.resolve(
createAppDetailResponse({
enable_site: true,
updated_at: 1781660300,
updated_by: 'user-3',
}),
)
await waitFor(() => {
expect(queryClient.getQueryData(['agent-detail', 'agent-1'])).toMatchObject({
enable_site: true,
updated_at: 1781660300,
updated_by: 'user-3',
})
})
expect(
await screen.findByRole('link', {
name: 'agentV2.agentDetail.access.webApp.actions.launch',
}),
).toHaveAttribute('href', 'https://chat.example.test/agent/site-token')
expect(toast.success).not.toHaveBeenCalled()
})
it('should keep launch disabled while enabling is pending and roll back after failure', async () => {
const user = userEvent.setup()
const toggle = createDeferredPromise<AppDetail>()
mocks.siteEnableMutation.mockReturnValueOnce(toggle.promise)
renderWithQueryClient(
<WebAppAccessCard
agent={createAgent({ enable_site: false })}
agentId="agent-1"
isLoading={false}
/>,
)
const accessSwitch = screen.getByRole('switch', {
name: 'agentV2.agentDetail.access.toggleSurface:{"name":"agentV2.agentDetail.access.webApp.title"}',
})
const launchButton = screen.getByRole('button', {
name: 'agentV2.agentDetail.access.webApp.actions.launch',
})
await user.click(accessSwitch)
expect(accessSwitch).toHaveAttribute('aria-checked', 'true')
expect(accessSwitch).toBeEnabled()
expect(launchButton).toBeDisabled()
expect(
screen.queryByRole('link', { name: 'agentV2.agentDetail.access.webApp.actions.launch' }),
).not.toBeInTheDocument()
toggle.reject(new Error('request failed'))
await waitFor(() => {
expect(accessSwitch).toHaveAttribute('aria-checked', 'false')
})
expect(launchButton).toBeDisabled()
expect(toast.error).toHaveBeenCalledWith('common.actionMsg.modifiedUnsuccessfully')
})
it('should open the customize dialog with the backing app id and API base URL', async () => {
@@ -616,24 +767,56 @@ describe('Agent access surface cards', () => {
describe('Service API access', () => {
it('should render service API data and toggle Agent API status through the generated Agent endpoint', async () => {
const user = userEvent.setup()
mocks.apiAccessQueryFn.mockResolvedValueOnce({
access_ready: true,
api_key_count: 2,
enabled: true,
service_api_base_url: 'https://api.example.test/v1',
})
mocks.apiEnableMutation.mockResolvedValueOnce({
access_ready: true,
api_key_count: 2,
enabled: false,
service_api_base_url: 'https://api.example.test/v1',
})
const toggle = createDeferredPromise<AgentApiAccessResponse>()
mocks.apiAccessQueryFn.mockResolvedValueOnce(createAgentApiAccessResponse())
mocks.apiEnableMutation.mockReturnValueOnce(toggle.promise)
renderWithQueryClient(<ServiceApiAccessCard agentId="agent-1" />)
expect(await screen.findByText('https://api.example.test/v1')).toBeInTheDocument()
expect(screen.getByText('2')).toBeInTheDocument()
const accessSwitch = screen.getByRole('switch', {
name: 'agentV2.agentDetail.access.toggleSurface:{"name":"agentV2.agentDetail.access.serviceApi.title"}',
})
const apiKeyButton = screen.getByRole('button', {
name: /agentV2\.agentDetail\.access\.serviceApi\.actions\.apiKey/,
})
await user.click(accessSwitch)
expect(accessSwitch).toHaveAttribute('aria-checked', 'false')
expect(accessSwitch).toBeEnabled()
expect(apiKeyButton).toBeEnabled()
expect(mocks.apiEnableMutation.mock.calls[0]?.[0]).toEqual({
params: {
agent_id: 'agent-1',
},
body: {
enable_api: false,
},
})
toggle.reject(new Error('request failed'))
await waitFor(() => {
expect(accessSwitch).toHaveAttribute('aria-checked', 'true')
})
expect(toast.error).toHaveBeenCalledWith('common.actionMsg.modifiedUnsuccessfully')
expect(toast.success).not.toHaveBeenCalled()
})
it('should keep raw API enablement separate from effective API access status', async () => {
const user = userEvent.setup()
const initialApiAccess = createAgentApiAccessResponse({ enabled: false })
const updatedApiAccess = createAgentApiAccessResponse({ access_ready: false, enabled: false })
mocks.apiAccessQueryFn.mockResolvedValueOnce(initialApiAccess)
mocks.apiEnableMutation.mockResolvedValueOnce(updatedApiAccess)
const agent = createAgent({ enable_api: false })
const queryClient = renderWithQueryClient(<ServiceApiAccessCard agentId="agent-1" />)
queryClient.setQueryData(['agent-detail', 'agent-1'], agent)
await screen.findByText(initialApiAccess.service_api_base_url)
await user.click(
screen.getByRole('switch', {
name: 'agentV2.agentDetail.access.toggleSurface:{"name":"agentV2.agentDetail.access.serviceApi.title"}',
@@ -641,25 +824,17 @@ describe('Agent access surface cards', () => {
)
await waitFor(() => {
expect(mocks.apiEnableMutation.mock.calls[0]?.[0]).toEqual({
params: {
agent_id: 'agent-1',
},
body: {
enable_api: false,
},
expect(queryClient.getQueryData(['agent-api-access', 'agent-1'])).toEqual(updatedApiAccess)
expect(queryClient.getQueryData(['agent-detail', 'agent-1'])).toMatchObject({
enable_api: true,
})
})
expect(toast.success).not.toHaveBeenCalled()
})
it('should manage API keys with the Agent API key endpoints', async () => {
const user = userEvent.setup()
mocks.apiAccessQueryFn.mockResolvedValue({
access_ready: true,
api_key_count: 1,
enabled: true,
service_api_base_url: 'https://api.example.test/v1',
})
mocks.apiAccessQueryFn.mockResolvedValue(createAgentApiAccessResponse({ api_key_count: 1 }))
mocks.apiKeysQueryFn.mockResolvedValue({
data: [
{
@@ -721,12 +896,13 @@ describe('Agent access surface cards', () => {
it('should explain that publishing enables the Service API switch', async () => {
const user = userEvent.setup()
mocks.apiAccessQueryFn.mockResolvedValueOnce({
access_ready: false,
api_key_count: 0,
enabled: false,
service_api_base_url: 'https://api.example.test/v1',
})
mocks.apiAccessQueryFn.mockResolvedValueOnce(
createAgentApiAccessResponse({
access_ready: false,
api_key_count: 0,
enabled: false,
}),
)
renderWithQueryClient(<ServiceApiAccessCard agentId="agent-1" />)
@@ -25,12 +25,8 @@ export type AccessSurfaceCardProps = {
endpointActions?: ReactNode
disabled?: boolean
disabledReason?: string
busy?: boolean
}
export const accessSurfaceActionClassName =
'inline-flex h-8 items-center justify-center gap-1.5 whitespace-nowrap rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 text-[13px] leading-4 font-medium text-components-button-secondary-text shadow-xs outline-hidden backdrop-blur-[5px] hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid'
export function AccessSurfaceCard({
title,
icon,
@@ -45,7 +41,6 @@ export function AccessSurfaceCard({
endpointActions,
disabled = false,
disabledReason,
busy = false,
}: AccessSurfaceCardProps) {
const { t } = useTranslation('agentV2')
const { t: tCommon } = useTranslation('common')
@@ -62,13 +57,13 @@ export function AccessSurfaceCard({
<Switch
size="md"
checked={enabled}
disabled={busy || (disabled && !hasDisabledReason)}
disabled={disabled && !hasDisabledReason}
readOnly={hasDisabledReason}
aria-disabled={hasDisabledReason || undefined}
data-disabled={hasDisabledReason ? '' : undefined}
aria-label={t(($) => $['agentDetail.access.toggleSurface'], { name: title })}
onCheckedChange={(nextEnabled) => {
if (!disabled && !busy) onEnabledChange(nextEnabled)
if (!disabled) onEnabledChange(nextEnabled)
}}
/>
)
@@ -1,14 +1,14 @@
'use client'
import type { AgentAppDetailWithSite } from '@dify/contracts/api/console/agent/types.gen'
import { Button } from '@langgenius/dify-ui/button'
import { Button, buttonVariants } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useDocLink } from '@/context/i18n'
import { consoleQuery } from '@/service/client'
import { accessSurfaceActionClassName, AccessSurfaceCard } from './access-surface-card'
import { AccessSurfaceCard } from './access-surface-card'
import { AgentApiKeyModal } from './agent-api-key-modal'
export function ServiceApiAccessCard({ agentId }: { agentId: string }) {
@@ -28,6 +28,9 @@ export function ServiceApiAccessCard({ agentId }: { agentId: string }) {
const apiAccess = apiAccessQuery.data
const toggleServiceApiMutation = useMutation(
consoleQuery.agent.byAgentId.apiEnable.post.mutationOptions({
scope: {
id: `agent-service-api-toggle:${agentId}`,
},
onSuccess: (updatedApiAccess, variables) => {
queryClient.setQueryData(apiAccessQueryOptions.queryKey, updatedApiAccess)
queryClient.setQueryData<AgentAppDetailWithSite | undefined>(
@@ -40,7 +43,6 @@ export function ServiceApiAccessCard({ agentId }: { agentId: string }) {
}
: agentDetail,
)
toast.success(tCommon(($) => $['actionMsg.modifiedSuccessfully']))
},
onError: () => {
toast.error(tCommon(($) => $['actionMsg.modifiedUnsuccessfully']))
@@ -48,7 +50,11 @@ export function ServiceApiAccessCard({ agentId }: { agentId: string }) {
}),
)
const accessReady = Boolean(apiAccess?.access_ready)
const isBusy = apiAccessQuery.isPending || toggleServiceApiMutation.isPending
const pendingEnabled = toggleServiceApiMutation.variables?.body.enable_api
const optimisticEnabled =
toggleServiceApiMutation.isPending && pendingEnabled !== undefined
? pendingEnabled
: Boolean(apiAccess?.enabled)
const showPublishRequiredMessage =
!apiAccessQuery.isPending && !apiAccessQuery.isError && !accessReady
@@ -71,20 +77,18 @@ export function ServiceApiAccessCard({ agentId }: { agentId: string }) {
iconClassName="bg-state-accent-solid text-text-primary-on-surface"
endpointLabel={t(($) => $['agentDetail.access.serviceApi.endpoint'])}
endpoint={apiAccess?.service_api_base_url ?? ''}
enabled={Boolean(apiAccess?.enabled)}
enabled={optimisticEnabled}
onEnabledChange={handleEnabledChange}
copyLabel={t(($) => $['agentDetail.access.copyServiceEndpoint'])}
disabled={apiAccessQuery.isPending || apiAccessQuery.isError || !accessReady}
disabledReason={
showPublishRequiredMessage ? t(($) => $['agentDetail.access.publishRequired']) : undefined
}
busy={toggleServiceApiMutation.isPending}
>
<Button
variant="secondary"
size="medium"
className="px-3"
disabled={isBusy || apiAccessQuery.isError || !accessReady}
disabled={apiAccessQuery.isPending || apiAccessQuery.isError || !accessReady}
onClick={() => setApiKeyModalOpen(true)}
>
<span aria-hidden className="i-ri-key-2-line size-4" />
@@ -98,7 +102,7 @@ export function ServiceApiAccessCard({ agentId }: { agentId: string }) {
target="_blank"
rel="noreferrer"
aria-label={t(($) => $['agentDetail.access.serviceApi.actions.apiReference'])}
className={accessSurfaceActionClassName}
className={buttonVariants({ variant: 'secondary', size: 'medium' })}
>
<span aria-hidden className="i-ri-book-open-line size-4" />
{t(($) => $['agentDetail.access.serviceApi.actions.apiReference'])}
@@ -107,7 +111,6 @@ export function ServiceApiAccessCard({ agentId }: { agentId: string }) {
<Button
variant="secondary"
size="medium"
className="px-3"
onClick={() => {
void apiAccessQuery.refetch()
}}
@@ -4,7 +4,7 @@ import type { AgentAppDetailWithSite } from '@dify/contracts/api/console/agent/t
import type { AppSiteUpdatePayload } from '@dify/contracts/api/console/apps/types.gen'
import type { ConfigParams, SettingsAppInfo } from '@/app/components/app/overview/settings'
import type { AppIconType } from '@/types/app'
import { Button } from '@langgenius/dify-ui/button'
import { Button, buttonVariants } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useMutation, useQueryClient } from '@tanstack/react-query'
@@ -17,9 +17,60 @@ import ShareQRCode from '@/app/components/base/qrcode'
import { AccessMode } from '@/models/access-control'
import { consoleQuery } from '@/service/client'
import { AppModeEnum } from '@/types/app'
import { accessSurfaceActionClassName, AccessSurfaceCard } from './access-surface-card'
import { AccessSurfaceCard } from './access-surface-card'
import { WebAppAccessControlButton } from './web-app-access-control-button'
function WebAppLaunchAction({
href,
label,
disabledReason,
}: {
href?: string
label: string
disabledReason?: string
}) {
const content = (
<>
<span aria-hidden className="i-ri-external-link-line size-4" />
{label}
</>
)
if (href) {
return (
<a
href={href}
target="_blank"
rel="noreferrer"
aria-label={label}
className={buttonVariants({ variant: 'secondary', size: 'medium' })}
>
{content}
</a>
)
}
const disabledButton = (
<Button
variant="secondary"
size="medium"
disabled
focusableWhenDisabled={Boolean(disabledReason)}
>
{content}
</Button>
)
if (!disabledReason) return disabledButton
return (
<Tooltip>
<TooltipTrigger render={disabledButton} />
<TooltipContent role="tooltip">{disabledReason}</TooltipContent>
</Tooltip>
)
}
export function WebAppAccessCard({
agent,
agentId,
@@ -39,7 +90,6 @@ export function WebAppAccessCard({
const appBaseUrl =
site?.app_base_url || (typeof window === 'undefined' ? '' : window.location.origin)
const webAppUrl = getAgentWebAppUrl(agent)
const isEnabled = Boolean(agent?.enable_site)
const accessReady = Boolean(agent?.access_ready)
const canManageWebApp = Boolean(appId && accessReady)
const embeddedConfig =
@@ -71,19 +121,22 @@ export function WebAppAccessCard({
})
const toggleSiteMutation = useMutation(
consoleQuery.apps.byAppId.siteEnable.post.mutationOptions({
onSuccess: async (_updatedApp, variables) => {
scope: {
id: `agent-web-app-toggle:${agentId}`,
},
onSuccess: (updatedApp) => {
queryClient.setQueryData<AgentAppDetailWithSite | undefined>(
agentDetailQueryKey,
(agentDetail) =>
agentDetail
? {
...agentDetail,
enable_site: variables.body.enable_site,
enable_site: updatedApp.enable_site,
updated_at: updatedApp.updated_at,
updated_by: updatedApp.updated_by,
}
: agentDetail,
)
await queryClient.invalidateQueries({ queryKey: agentDetailQueryKey })
toast.success(tCommon(($) => $['actionMsg.modifiedSuccessfully']))
},
onError: () => {
toast.error(tCommon(($) => $['actionMsg.modifiedUnsuccessfully']))
@@ -116,24 +169,15 @@ export function WebAppAccessCard({
}),
)
const updateSiteMutation = useMutation(consoleQuery.apps.byAppId.site.post.mutationOptions())
const isBusy =
toggleSiteMutation.isPending ||
resetAccessTokenMutation.isPending ||
updateSiteMutation.isPending
const pendingEnabled = toggleSiteMutation.variables?.body.enable_site
const optimisticEnabled =
toggleSiteMutation.isPending && pendingEnabled !== undefined
? pendingEnabled
: Boolean(agent?.enable_site)
const launchHref =
webAppUrl && agent?.enable_site && !toggleSiteMutation.isPending ? webAppUrl : undefined
const publishRequiredMessage = t(($) => $['agentDetail.access.publishRequired'])
const showPublishRequiredMessage = !isLoading && !accessReady
const disabledLaunchButton = (
<Button
variant="secondary"
size="medium"
className="px-3"
disabled
focusableWhenDisabled={showPublishRequiredMessage}
>
<span aria-hidden className="i-ri-external-link-line size-4" />
{t(($) => $['agentDetail.access.webApp.actions.launch'])}
</Button>
)
function handleEnabledChange(enabled: boolean) {
if (!appId) return
@@ -212,7 +256,7 @@ export function WebAppAccessCard({
iconClassName="bg-state-accent-solid text-text-primary-on-surface"
endpointLabel={t(($) => $['agentDetail.access.webApp.accessUrl'])}
endpoint={webAppUrl}
enabled={isEnabled}
enabled={optimisticEnabled}
onEnabledChange={handleEnabledChange}
copyLabel={t(($) => $['agentDetail.access.copyAccessUrl'])}
badge={showSsoBadge ? <SsoBadge /> : undefined}
@@ -226,7 +270,7 @@ export function WebAppAccessCard({
size="small"
className="size-6 shrink-0 px-0 text-text-tertiary hover:text-text-secondary"
aria-label={t(($) => $['agentDetail.access.webApp.refreshUrl'])}
disabled={!canManageWebApp || isBusy}
disabled={!canManageWebApp || resetAccessTokenMutation.isPending}
onClick={handleRefreshUrl}
>
<span aria-hidden className="i-ri-refresh-line size-4" />
@@ -236,31 +280,15 @@ export function WebAppAccessCard({
}
disabled={isLoading || !canManageWebApp}
disabledReason={showPublishRequiredMessage ? publishRequiredMessage : undefined}
busy={isBusy}
>
{webAppUrl && isEnabled ? (
<a
href={webAppUrl}
target="_blank"
rel="noreferrer"
aria-label={t(($) => $['agentDetail.access.webApp.actions.launch'])}
className={accessSurfaceActionClassName}
>
<span aria-hidden className="i-ri-external-link-line size-4" />
{t(($) => $['agentDetail.access.webApp.actions.launch'])}
</a>
) : showPublishRequiredMessage ? (
<Tooltip>
<TooltipTrigger render={disabledLaunchButton} />
<TooltipContent role="tooltip">{publishRequiredMessage}</TooltipContent>
</Tooltip>
) : (
disabledLaunchButton
)}
<WebAppLaunchAction
href={launchHref}
label={t(($) => $['agentDetail.access.webApp.actions.launch'])}
disabledReason={showPublishRequiredMessage ? publishRequiredMessage : undefined}
/>
<Button
variant="secondary"
size="medium"
className="px-3"
disabled={!embeddedConfig}
onClick={() => setShowEmbeddedModal(true)}
>
@@ -270,7 +298,6 @@ export function WebAppAccessCard({
<Button
variant="secondary"
size="medium"
className="px-3"
disabled={!customizeConfig}
onClick={() => setShowCustomizeModal(true)}
>
@@ -280,7 +307,6 @@ export function WebAppAccessCard({
<Button
variant="secondary"
size="medium"
className="px-3"
disabled={!settingsAppInfo || updateSiteMutation.isPending}
onClick={() => setShowSettingsModal(true)}
>
@@ -45,12 +45,7 @@ export function WebAppAccessControlButton({ agent }: { agent?: AgentAppDetailWit
return (
<>
<Button
variant="secondary"
size="medium"
className="px-3"
onClick={() => setShowAccessControl(true)}
>
<Button variant="secondary" size="medium" onClick={() => setShowAccessControl(true)}>
<span aria-hidden className="i-ri-lock-2-line size-4" />
{t(($) => $['agentDetail.access.webApp.actions.accessControl'])}
</Button>