From fb6793d05f068f2e6d168f2ceae0e708982982a9 Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Mon, 15 Jun 2026 21:44:08 +0700 Subject: [PATCH] fix(admin-ui): patch site config updates instead of replacing The admin UI sends partial SiteConfig bodies from Configuration and Banner Builder tabs. Using PUT replaced omitted fields with backend defaults, which reset default_language to null whenever banner_config was saved. Switch updateSiteConfig() to PATCH so omitted fields are preserved, make the new default_language field optional for older config shapes, and add a regression test that ensures partial updates do not use PUT. --- apps/admin-ui/src/api/sites.ts | 2 +- apps/admin-ui/src/test/sites-api.test.ts | 31 ++++++++++++++++++++++++ apps/admin-ui/src/types/api.ts | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 apps/admin-ui/src/test/sites-api.test.ts diff --git a/apps/admin-ui/src/api/sites.ts b/apps/admin-ui/src/api/sites.ts index d4f484b..d027287 100644 --- a/apps/admin-ui/src/api/sites.ts +++ b/apps/admin-ui/src/api/sites.ts @@ -38,7 +38,7 @@ export async function updateSiteConfig( siteId: string, body: Partial, ): Promise { - const { data } = await apiClient.put(`/sites/${siteId}/config`, body); + const { data } = await apiClient.patch(`/sites/${siteId}/config`, body); return data; } diff --git a/apps/admin-ui/src/test/sites-api.test.ts b/apps/admin-ui/src/test/sites-api.test.ts new file mode 100644 index 0000000..fce94e6 --- /dev/null +++ b/apps/admin-ui/src/test/sites-api.test.ts @@ -0,0 +1,31 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const apiClient = { + get: vi.fn(), + post: vi.fn(), + patch: vi.fn(), + put: vi.fn(), + delete: vi.fn(), +}; + +vi.mock('../api/client', () => ({ + default: apiClient, +})); + +describe('sites API client', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('partially updates site config with PATCH so omitted fields are preserved', async () => { + apiClient.patch.mockResolvedValue({ data: { id: 'cfg-1' } }); + + const { updateSiteConfig } = await import('../api/sites'); + await updateSiteConfig('site-1', { banner_config: { displayMode: 'corner_popup' } }); + + expect(apiClient.patch).toHaveBeenCalledWith('/sites/site-1/config', { + banner_config: { displayMode: 'corner_popup' }, + }); + expect(apiClient.put).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/admin-ui/src/types/api.ts b/apps/admin-ui/src/types/api.ts index cbe23bb..2ef4a9b 100644 --- a/apps/admin-ui/src/types/api.ts +++ b/apps/admin-ui/src/types/api.ts @@ -125,7 +125,7 @@ export interface SiteConfig { banner_config: BannerConfig | null; privacy_policy_url: string | null; terms_url: string | null; - default_language: string | null; + default_language?: string | null; consent_expiry_days: number; scan_enabled: boolean; scan_frequency_hours: number;