feat(home): add compact home page preset to avoid abuse classification

- Add compact_home_enabled setting to provide a minimal landing page
- Preserves custom home_content priority over compact mode
- Renders only site identity, navigation, and login/dashboard link
- Avoids marketing copy that triggers anti-fraud systems
- Includes focused backend/frontend tests and i18n support

Fixes #5065
This commit is contained in:
wucm667
2026-07-30 16:28:19 +08:00
parent 5a6143097d
commit 739c0ff9c5
25 changed files with 298 additions and 1 deletions
@@ -233,6 +233,7 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
ContactInfo: settings.ContactInfo,
DocURL: settings.DocURL,
HomeContent: settings.HomeContent,
CompactHomeEnabled: settings.CompactHomeEnabled,
HideCcsImportButton: settings.HideCcsImportButton,
PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled,
PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL,
@@ -299,6 +299,9 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
if before.HomeContent != after.HomeContent {
changed = append(changed, "home_content")
}
if before.CompactHomeEnabled != after.CompactHomeEnabled {
changed = append(changed, "compact_home_enabled")
}
if before.HideCcsImportButton != after.HideCcsImportButton {
changed = append(changed, "hide_ccs_import_button")
}
@@ -64,6 +64,18 @@ func TestDiffSettings_NoChangeWhenEqual(t *testing.T) {
}
}
func TestDiffSettings_DetectsCompactHomeChange(t *testing.T) {
changed := diffSettings(
&service.SystemSettings{},
&service.SystemSettings{CompactHomeEnabled: true},
nil,
nil,
UpdateSettingsRequest{},
)
require.Contains(t, changed, service.SettingKeyCompactHomeEnabled)
}
func TestEqualNullableFloat(t *testing.T) {
five := 5.0
five2 := 5.0
@@ -142,6 +142,7 @@ type UpdateSettingsRequest struct {
ContactInfo string `json:"contact_info"`
DocURL string `json:"doc_url"`
HomeContent string `json:"home_content"`
CompactHomeEnabled bool `json:"compact_home_enabled"`
HideCcsImportButton bool `json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled *bool `json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL *string `json:"purchase_subscription_url"`
@@ -1430,6 +1431,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
ContactInfo: req.ContactInfo,
DocURL: req.DocURL,
HomeContent: req.HomeContent,
CompactHomeEnabled: req.CompactHomeEnabled,
HideCcsImportButton: req.HideCcsImportButton,
PurchaseSubscriptionEnabled: purchaseEnabled,
PurchaseSubscriptionURL: purchaseURL,
@@ -1982,6 +1984,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
ContactInfo: updatedSettings.ContactInfo,
DocURL: updatedSettings.DocURL,
HomeContent: updatedSettings.HomeContent,
CompactHomeEnabled: updatedSettings.CompactHomeEnabled,
HideCcsImportButton: updatedSettings.HideCcsImportButton,
PurchaseSubscriptionEnabled: updatedSettings.PurchaseSubscriptionEnabled,
PurchaseSubscriptionURL: updatedSettings.PurchaseSubscriptionURL,
+2
View File
@@ -142,6 +142,7 @@ type SystemSettings struct {
ContactInfo string `json:"contact_info"`
DocURL string `json:"doc_url"`
HomeContent string `json:"home_content"`
CompactHomeEnabled bool `json:"compact_home_enabled"`
HideCcsImportButton bool `json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL string `json:"purchase_subscription_url"`
@@ -344,6 +345,7 @@ type PublicSettings struct {
ContactInfo string `json:"contact_info"`
DocURL string `json:"doc_url"`
HomeContent string `json:"home_content"`
CompactHomeEnabled bool `json:"compact_home_enabled"`
HideCcsImportButton bool `json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL string `json:"purchase_subscription_url"`
@@ -67,6 +67,7 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) {
ContactInfo: settings.ContactInfo,
DocURL: settings.DocURL,
HomeContent: settings.HomeContent,
CompactHomeEnabled: settings.CompactHomeEnabled,
HideCcsImportButton: settings.HideCcsImportButton,
PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled,
PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL,
@@ -271,6 +271,7 @@ const (
SettingKeyContactInfo = "contact_info" // 客服联系方式
SettingKeyDocURL = "doc_url" // 文档链接
SettingKeyHomeContent = "home_content" // 首页内容(支持 Markdown/HTML,或 URL 作为 iframe src)
SettingKeyCompactHomeEnabled = "compact_home_enabled" // 是否启用内置简洁首页
SettingKeyHideCcsImportButton = "hide_ccs_import_button" // 是否隐藏 API Keys 页面的导入 CCS 按钮
SettingKeyPurchaseSubscriptionEnabled = "purchase_subscription_enabled" // 是否展示"购买订阅"页面入口
SettingKeyPurchaseSubscriptionURL = "purchase_subscription_url" // "购买订阅"页面 URL(作为 iframe src)
@@ -330,6 +330,7 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
ContactInfo: settings[SettingKeyContactInfo],
DocURL: settings[SettingKeyDocURL],
HomeContent: settings[SettingKeyHomeContent],
CompactHomeEnabled: settings[SettingKeyCompactHomeEnabled] == "true",
HideCcsImportButton: settings[SettingKeyHideCcsImportButton] == "true",
PurchaseSubscriptionEnabled: settings[SettingKeyPurchaseSubscriptionEnabled] == "true",
PurchaseSubscriptionURL: strings.TrimSpace(settings[SettingKeyPurchaseSubscriptionURL]),
@@ -179,6 +179,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
SettingKeyContactInfo,
SettingKeyDocURL,
SettingKeyHomeContent,
SettingKeyCompactHomeEnabled,
SettingKeyHideCcsImportButton,
SettingKeyPurchaseSubscriptionEnabled,
SettingKeyPurchaseSubscriptionURL,
@@ -307,6 +308,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
ContactInfo: settings[SettingKeyContactInfo],
DocURL: settings[SettingKeyDocURL],
HomeContent: settings[SettingKeyHomeContent],
CompactHomeEnabled: settings[SettingKeyCompactHomeEnabled] == "true",
HideCcsImportButton: settings[SettingKeyHideCcsImportButton] == "true",
PurchaseSubscriptionEnabled: settings[SettingKeyPurchaseSubscriptionEnabled] == "true",
PurchaseSubscriptionURL: strings.TrimSpace(settings[SettingKeyPurchaseSubscriptionURL]),
@@ -495,6 +497,7 @@ type PublicSettingsInjectionPayload struct {
ContactInfo string `json:"contact_info"`
DocURL string `json:"doc_url"`
HomeContent string `json:"home_content"`
CompactHomeEnabled bool `json:"compact_home_enabled"`
HideCcsImportButton bool `json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL string `json:"purchase_subscription_url"`
@@ -567,6 +570,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any
ContactInfo: settings.ContactInfo,
DocURL: settings.DocURL,
HomeContent: settings.HomeContent,
CompactHomeEnabled: settings.CompactHomeEnabled,
HideCcsImportButton: settings.HideCcsImportButton,
PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled,
PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL,
@@ -78,6 +78,25 @@ func TestSettingService_GetPublicSettings_ExposesTablePreferences(t *testing.T)
require.Equal(t, []int{20, 50, 100}, settings.TablePageSizeOptions)
}
func TestSettingService_GetPublicSettings_ExposesCompactHomeEnabled(t *testing.T) {
repo := &settingPublicRepoStub{
values: map[string]string{
SettingKeyCompactHomeEnabled: "true",
},
}
svc := NewSettingService(repo, &config.Config{})
settings, err := svc.GetPublicSettings(context.Background())
require.NoError(t, err)
require.True(t, settings.CompactHomeEnabled)
missingSettings, err := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).
GetPublicSettings(context.Background())
require.NoError(t, err)
require.False(t, missingSettings.CompactHomeEnabled)
}
func TestSettingService_GetPublicSettings_ExposesForceEmailOnThirdPartySignup(t *testing.T) {
repo := &settingPublicRepoStub{
values: map[string]string{
@@ -228,6 +228,16 @@ func (s *defaultSubGroupReaderStub) GetByID(ctx context.Context, id int64) (*Gro
return nil, ErrGroupNotFound
}
func TestSettingService_UpdateSettings_PersistsCompactHomeEnabled(t *testing.T) {
repo := &settingUpdateRepoStub{}
svc := NewSettingService(repo, &config.Config{})
err := svc.UpdateSettings(context.Background(), &SystemSettings{CompactHomeEnabled: true})
require.NoError(t, err)
require.Equal(t, "true", repo.updates[SettingKeyCompactHomeEnabled])
}
func TestSettingService_UpdateSettings_DefaultSubscriptions_ValidGroup(t *testing.T) {
repo := &settingUpdateRepoStub{}
groupReader := &defaultSubGroupReaderStub{
@@ -320,6 +320,7 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
updates[SettingKeyContactInfo] = settings.ContactInfo
updates[SettingKeyDocURL] = settings.DocURL
updates[SettingKeyHomeContent] = settings.HomeContent
updates[SettingKeyCompactHomeEnabled] = strconv.FormatBool(settings.CompactHomeEnabled)
updates[SettingKeyHideCcsImportButton] = strconv.FormatBool(settings.HideCcsImportButton)
updates[SettingKeyPurchaseSubscriptionEnabled] = strconv.FormatBool(settings.PurchaseSubscriptionEnabled)
updates[SettingKeyPurchaseSubscriptionURL] = strings.TrimSpace(settings.PurchaseSubscriptionURL)
@@ -139,6 +139,7 @@ type SystemSettings struct {
ContactInfo string
DocURL string
HomeContent string
CompactHomeEnabled bool
HideCcsImportButton bool
PurchaseSubscriptionEnabled bool
PurchaseSubscriptionURL string
@@ -305,6 +306,7 @@ type PublicSettings struct {
ContactInfo string
DocURL string
HomeContent string
CompactHomeEnabled bool
HideCcsImportButton bool
PurchaseSubscriptionEnabled bool
+2
View File
@@ -440,6 +440,7 @@ export interface SystemSettings {
contact_info: string;
doc_url: string;
home_content: string;
compact_home_enabled: boolean;
hide_ccs_import_button: boolean;
table_default_page_size: number;
table_page_size_options: number[];
@@ -752,6 +753,7 @@ export interface UpdateSettingsRequest {
contact_info?: string;
doc_url?: string;
home_content?: string;
compact_home_enabled?: boolean;
hide_ccs_import_button?: boolean;
table_default_page_size?: number;
table_page_size_options?: number[];
@@ -73,6 +73,7 @@ function buildPublicSettings(overrides: Partial<WeChatPublicSettings> = {}): WeC
contact_info: '',
doc_url: '',
home_content: '',
compact_home_enabled: false,
hide_ccs_import_button: false,
payment_enabled: false,
table_default_page_size: 20,
@@ -250,6 +250,7 @@ describe('ProfileIdentityBindingsSection', () => {
contact_info: '',
doc_url: '',
home_content: '',
compact_home_enabled: false,
hide_ccs_import_button: false,
payment_enabled: false,
table_default_page_size: 20,
@@ -548,6 +548,8 @@ export default {
homeContentPlaceholder: 'Enter custom content for the home page. Supports Markdown & HTML. If a URL is entered, it will be displayed as an iframe.',
homeContentHint: 'Customize the home page content. Supports Markdown/HTML. If you enter a URL (starting with http:// or https://), it will be used as an iframe src to embed an external page. When set, the default status information will no longer be displayed.',
homeContentIframeWarning: '⚠️ iframe mode note: Some websites have X-Frame-Options or CSP security policies that prevent embedding in iframes. If the page appears blank or shows an error, please verify the target website allows embedding, or consider using HTML mode to build your own content.',
compactHome: 'Compact Home Page',
compactHomeHint: 'Show a restrained site identity page when no custom home page content is set.',
hideCcsImportButton: 'Hide CCS Import Button',
hideCcsImportButtonHint: 'When enabled, the "Import to CCS" button will be hidden on the API Keys page'
},
@@ -543,6 +543,8 @@ export default {
'自定义首页内容,支持 Markdown/HTML。如果输入的是链接(以 http:// 或 https:// 开头),则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页。设置后首页的状态信息将不再显示。',
homeContentIframeWarning:
'⚠️ iframe 模式提示:部分网站设置了 X-Frame-Options 或 CSP 安全策略,禁止被嵌入到 iframe 中。如果页面显示空白或报错,请确认目标网站允许被嵌入,或考虑使用 HTML 模式自行构建页面内容。',
compactHome: '简洁首页',
compactHomeHint: '未设置自定义首页内容时,展示简洁的站点信息页面。',
hideCcsImportButton: '隐藏 CCS 导入按钮',
hideCcsImportButtonHint: '启用后将在 API Keys 页面隐藏"导入 CCS"按钮'
},
@@ -33,6 +33,7 @@ function createPublicSettings(overrides: Partial<PublicSettings> = {}): PublicSe
contact_info: '',
doc_url: '',
home_content: '',
compact_home_enabled: false,
hide_ccs_import_button: false,
payment_enabled: false,
risk_control_enabled: false,
@@ -452,6 +453,7 @@ describe('useAppStore', () => {
contact_info: '',
doc_url: '',
home_content: '',
compact_home_enabled: false,
hide_ccs_import_button: false,
purchase_subscription_enabled: false,
purchase_subscription_url: '',
+1
View File
@@ -342,6 +342,7 @@ export const useAppStore = defineStore('app', () => {
contact_info: contactInfo.value,
doc_url: docUrl.value,
home_content: '',
compact_home_enabled: false,
hide_ccs_import_button: false,
payment_enabled: false,
table_default_page_size: 20,
+1
View File
@@ -210,6 +210,7 @@ export interface PublicSettings {
contact_info: string
doc_url: string
home_content: string
compact_home_enabled: boolean
hide_ccs_import_button: boolean
payment_enabled: boolean
risk_control_enabled: boolean
+72 -1
View File
@@ -1,6 +1,6 @@
<template>
<!-- Custom Home Content: Full Page Mode -->
<div v-if="homeContent" class="min-h-screen">
<div v-if="hasHomeContent" class="min-h-screen">
<!-- iframe mode -->
<iframe
v-if="isHomeContentUrl"
@@ -12,6 +12,75 @@
<div v-else v-html="homeContent"></div>
</div>
<!-- Compact Home Page -->
<div
v-else-if="compactHomeEnabled"
data-testid="compact-home"
class="flex min-h-screen flex-col bg-gray-50 text-gray-900 dark:bg-dark-950 dark:text-white"
>
<header class="border-b border-gray-200 px-4 py-4 sm:px-6 dark:border-dark-800">
<nav class="mx-auto flex max-w-5xl flex-wrap items-center justify-between gap-3 sm:gap-4">
<div class="flex min-w-0 flex-1 items-center gap-3">
<img
:src="siteLogo || '/logo.svg'"
alt="Logo"
class="h-9 w-9 shrink-0 rounded-lg object-contain"
/>
<span class="min-w-0 truncate text-base font-semibold">{{ siteName }}</span>
</div>
<div class="flex max-w-full shrink-0 flex-wrap items-center justify-end gap-2">
<LocaleSwitcher />
<a
v-if="docUrl"
:href="docUrl"
target="_blank"
rel="noopener noreferrer"
class="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-gray-500 hover:bg-gray-100 dark:text-dark-400 dark:hover:bg-dark-800"
:title="t('home.viewDocs')"
>
<Icon name="book" size="md" />
</a>
<button
class="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-gray-500 hover:bg-gray-100 dark:text-dark-400 dark:hover:bg-dark-800"
:title="isDark ? t('home.switchToLight') : t('home.switchToDark')"
@click="toggleTheme"
>
<Icon v-if="isDark" name="sun" size="md" />
<Icon v-else name="moon" size="md" />
</button>
<router-link
:to="isAuthenticated ? dashboardPath : '/login'"
class="inline-flex min-h-10 shrink-0 items-center justify-center rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-200"
>
{{ isAuthenticated ? t('home.dashboard') : t('home.login') }}
</router-link>
</div>
</nav>
</header>
<main class="flex min-w-0 flex-1 items-center justify-center px-4 py-16 sm:px-6">
<div class="min-w-0 max-w-2xl text-center">
<img
:src="siteLogo || '/logo.svg'"
alt="Logo"
class="mx-auto mb-6 h-20 w-20 rounded-2xl object-contain"
/>
<h1 class="[overflow-wrap:anywhere] text-3xl font-bold md:text-4xl">{{ siteName }}</h1>
<p class="mt-4 whitespace-pre-wrap [overflow-wrap:anywhere] text-base text-gray-600 dark:text-dark-300">{{ siteSubtitle }}</p>
<router-link
:to="isAuthenticated ? dashboardPath : '/login'"
class="mt-8 inline-flex min-h-10 items-center justify-center rounded-lg bg-primary-600 px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-700"
>
{{ isAuthenticated ? t('home.goToDashboard') : t('home.login') }}
</router-link>
</div>
</main>
<footer class="min-w-0 border-t border-gray-200 px-4 py-5 text-center text-sm text-gray-500 [overflow-wrap:anywhere] sm:px-6 dark:border-dark-800 dark:text-dark-400">
&copy; {{ currentYear }} {{ siteName }}
</footer>
</div>
<!-- Default Home Page -->
<div
v-else
@@ -423,6 +492,8 @@ const siteLogo = computed(() => sanitizeUrl(appStore.cachedPublicSettings?.site_
const siteSubtitle = computed(() => appStore.cachedPublicSettings?.site_subtitle || 'AI API Gateway Platform')
const docUrl = computed(() => sanitizeUrl(appStore.cachedPublicSettings?.doc_url || appStore.docUrl || ''))
const homeContent = computed(() => appStore.cachedPublicSettings?.home_content || '')
const hasHomeContent = computed(() => homeContent.value.trim().length > 0)
const compactHomeEnabled = computed(() => appStore.cachedPublicSettings?.compact_home_enabled === true)
// Check if homeContent is a URL (for iframe display)
const isHomeContentUrl = computed(() => {
@@ -0,0 +1,122 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { mount, RouterLinkStub } from '@vue/test-utils'
import HomeView from '../HomeView.vue'
const { appStore, authStore } = vi.hoisted(() => ({
appStore: {
cachedPublicSettings: {} as Record<string, unknown>,
siteName: 'Fallback site',
siteLogo: '',
docUrl: '',
publicSettingsLoaded: true,
fetchPublicSettings: vi.fn(),
},
authStore: {
isAuthenticated: false,
isAdmin: false,
user: null as { email?: string } | null,
checkAuth: vi.fn(),
},
}))
vi.mock('@/stores', () => ({
useAppStore: () => appStore,
useAuthStore: () => authStore,
}))
vi.mock('vue-i18n', async (importOriginal) => {
const actual = await importOriginal<typeof import('vue-i18n')>()
return {
...actual,
useI18n: () => ({ t: (key: string) => key }),
}
})
function mountHome(settings: Record<string, unknown> = {}) {
appStore.cachedPublicSettings = {
site_name: 'Test site',
site_subtitle: 'Test subtitle',
...settings,
}
return mount(HomeView, {
global: {
stubs: {
RouterLink: RouterLinkStub,
LocaleSwitcher: { template: '<div data-testid="locale-switcher" />' },
Icon: { template: '<span data-testid="icon" />' },
},
},
})
}
function compactDestination(wrapper: ReturnType<typeof mountHome>) {
return wrapper.get('[data-testid="compact-home"]').findComponent(RouterLinkStub).props('to')
}
describe('HomeView compact mode', () => {
beforeEach(() => {
authStore.isAuthenticated = false
authStore.isAdmin = false
authStore.user = null
authStore.checkAuth.mockClear()
appStore.fetchPublicSettings.mockClear()
localStorage.clear()
vi.spyOn(window, 'matchMedia').mockReturnValue({ matches: false } as MediaQueryList)
})
it('renders custom HTML ahead of compact mode', () => {
const wrapper = mountHome({
compact_home_enabled: true,
home_content: '<section id="custom-home">Custom home</section>',
})
expect(wrapper.get('#custom-home').text()).toBe('Custom home')
expect(wrapper.find('[data-testid="compact-home"]').exists()).toBe(false)
})
it('renders custom URL content ahead of compact mode', () => {
const wrapper = mountHome({
compact_home_enabled: true,
home_content: ' https://example.com/home ',
})
expect(wrapper.get('iframe').attributes('src')).toBe('https://example.com/home')
expect(wrapper.find('[data-testid="compact-home"]').exists()).toBe(false)
})
it('treats whitespace-only custom content as empty and selects compact mode', () => {
const wrapper = mountHome({ compact_home_enabled: true, home_content: ' \n\t ' })
expect(wrapper.get('[data-testid="compact-home"]').text()).toContain('Test site')
})
it.each([undefined, false])('selects the default home when compact mode is %s', (enabled) => {
const settings = enabled === undefined ? {} : { compact_home_enabled: enabled }
const wrapper = mountHome(settings)
expect(wrapper.find('[data-testid="compact-home"]').exists()).toBe(false)
expect(wrapper.find('.terminal-container').exists()).toBe(true)
})
it('links unauthenticated visitors to login', () => {
expect(compactDestination(mountHome({ compact_home_enabled: true }))).toBe('/login')
})
it('links authenticated users to their dashboard', () => {
authStore.isAuthenticated = true
expect(compactDestination(mountHome({ compact_home_enabled: true }))).toBe('/dashboard')
})
it('links administrators to the admin dashboard', () => {
authStore.isAuthenticated = true
authStore.isAdmin = true
const wrapper = mountHome({ compact_home_enabled: true })
expect(compactDestination(wrapper)).toBe('/admin/dashboard')
expect(authStore.checkAuth).toHaveBeenCalledOnce()
expect(appStore.fetchPublicSettings).not.toHaveBeenCalled()
})
})
+15
View File
@@ -5974,6 +5974,19 @@
</p>
</div>
<!-- Compact Home Page -->
<div class="flex items-center justify-between border-t border-gray-100 pt-4 dark:border-dark-700">
<div>
<label class="font-medium text-gray-900 dark:text-white">{{
t("admin.settings.site.compactHome")
}}</label>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{ t("admin.settings.site.compactHomeHint") }}
</p>
</div>
<Toggle v-model="form.compact_home_enabled" data-testid="compact-home-toggle" />
</div>
<!-- Hide CCS Import Button -->
<div
class="flex items-center justify-between border-t border-gray-100 pt-4 dark:border-dark-700"
@@ -8834,6 +8847,7 @@ const form = reactive<SettingsForm>({
contact_info: "",
doc_url: "",
home_content: "",
compact_home_enabled: false,
backend_mode_enabled: false,
hide_ccs_import_button: false,
payment_enabled: false,
@@ -10358,6 +10372,7 @@ async function saveSettings() {
contact_info: form.contact_info,
doc_url: form.doc_url,
home_content: form.home_content,
compact_home_enabled: form.compact_home_enabled,
backend_mode_enabled: form.backend_mode_enabled,
hide_ccs_import_button: form.hide_ccs_import_button,
table_default_page_size: form.table_default_page_size,
@@ -376,6 +376,7 @@ const baseSettingsResponse = {
contact_info: "",
doc_url: "",
home_content: "",
compact_home_enabled: false,
hide_ccs_import_button: false,
table_default_page_size: 20,
table_page_size_options: [10, 20, 50, 100],
@@ -678,6 +679,22 @@ describe("admin SettingsView payment visible method controls", () => {
adminSettingsFetch.mockResolvedValue(undefined);
});
it("submits the compact home page toggle", async () => {
const wrapper = mountView();
await flushPromises();
const toggle = wrapper.get('[data-testid="compact-home-toggle"]');
expect((toggle.element as HTMLInputElement).checked).toBe(false);
await toggle.setValue(true);
await wrapper.find("form").trigger("submit.prevent");
await flushPromises();
expect(updateSettings).toHaveBeenCalledWith(
expect.objectContaining({ compact_home_enabled: true }),
);
});
it("renders panel rate limit card and saves settings", async () => {
getPanelRateLimitSettings.mockClear();
updatePanelRateLimitSettings.mockClear();