From 960cd78b8a91537c5e5e1d58446b065d87279e41 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 26 May 2026 12:39:29 +0700 Subject: [PATCH] 690526:1239 ADR-023-229 dynamic prompt #03 --- .../access-control/organizations/page.tsx | 2 +- .../admin/access-control/users/page.tsx | 4 ++-- .../drawings/contract/categories/page.tsx | 2 +- .../doc-control/numbering/[id]/edit/page.tsx | 9 ++++---- .../admin/doc-control/numbering/new/page.tsx | 7 ++++--- .../admin/doc-control/numbering/page.tsx | 2 +- .../admin/ai/OcrSandboxPromptManager.tsx | 8 ++++++- frontend/lib/services/ai-prompts.service.ts | 21 +++++++++++++++---- 8 files changed, 38 insertions(+), 17 deletions(-) diff --git a/frontend/app/(admin)/admin/access-control/organizations/page.tsx b/frontend/app/(admin)/admin/access-control/organizations/page.tsx index 89bf2af6..a6dd2fec 100644 --- a/frontend/app/(admin)/admin/access-control/organizations/page.tsx +++ b/frontend/app/(admin)/admin/access-control/organizations/page.tsx @@ -80,7 +80,7 @@ export default function OrganizationsPage() { header: 'Role', cell: ({ row }) => { const roleId = row.getValue('roleId') as number; - const role = ORGANIZATION_ROLES.find((r) => r.value === roleId?.toString()); + const role = Array.isArray(ORGANIZATION_ROLES) ? ORGANIZATION_ROLES.find((r) => r.value === roleId?.toString()) : undefined; return role ? role.label : '-'; }, }, diff --git a/frontend/app/(admin)/admin/access-control/users/page.tsx b/frontend/app/(admin)/admin/access-control/users/page.tsx index d9b7259b..ef054728 100644 --- a/frontend/app/(admin)/admin/access-control/users/page.tsx +++ b/frontend/app/(admin)/admin/access-control/users/page.tsx @@ -99,9 +99,9 @@ export default function UsersPage() { return 'All Organizations'; } - const org = organizationList.find( + const org = Array.isArray(organizationList) ? organizationList.find( (o) => (o.id ?? o.publicId) === orgId?.toString() || o.publicId === orgId?.toString() - ); + ) : undefined; return org ? org.organizationCode : 'All Organizations'; }, }, diff --git a/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx b/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx index 0ce23401..0d9f50d3 100644 --- a/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx @@ -281,7 +281,7 @@ function ManageMappings({ projectId }: { projectId: string }) { {subCategories .filter( (s: ContractSubCategory) => - !mappings.find((m: Record) => { + !Array.isArray(mappings) || !mappings.find((m: Record) => { const sub = m.subCategory as { id?: number } | undefined; return sub?.id === s.id; }) diff --git a/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx b/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx index d3ec23d8..40de5919 100644 --- a/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx @@ -31,10 +31,11 @@ export default function EditTemplatePage() { const contractId = getContractPublicId(firstContract); const { data: disciplines = [] } = useDisciplines(contractId); - const selectedProjectName = - (projects as Array<{ id?: number; publicId?: string; projectCode: string; projectName: string }>).find((p) => - String(p.publicId ?? p.id) === String(projectId)) - ?.projectName || 'LCBP3'; + const selectedProjectName = Array.isArray(projects) + ? (projects as Array<{ id?: number; publicId?: string; projectCode: string; projectName: string }>).find((p) => + String(p.publicId ?? p.id) === String(projectId)) + ?.projectName || 'LCBP3' + : 'LCBP3'; useEffect(() => { const fetchTemplate = async () => { diff --git a/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx b/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx index ed57108d..c4671ed1 100644 --- a/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx @@ -21,9 +21,10 @@ export default function NewTemplatePage() { const contractId = getContractPublicId(firstContract); const { data: disciplines = [] } = useDisciplines(contractId); - const selectedProjectName = - (projects as Array<{ id?: number; publicId?: string; projectName: string }>).find((p) => - String(p.publicId ?? p.id) === String(projectId))?.projectName || 'LCBP3'; + const selectedProjectName = Array.isArray(projects) + ? (projects as Array<{ id?: number; publicId?: string; projectName: string }>).find((p) => + String(p.publicId ?? p.id) === String(projectId))?.projectName || 'LCBP3' + : 'LCBP3'; const handleSave = async (data: Partial) => { try { diff --git a/frontend/app/(admin)/admin/doc-control/numbering/page.tsx b/frontend/app/(admin)/admin/doc-control/numbering/page.tsx index b36834cf..3648ab2e 100644 --- a/frontend/app/(admin)/admin/doc-control/numbering/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/numbering/page.tsx @@ -49,7 +49,7 @@ export default function NumberingPage() { const [isTesting, setIsTesting] = useState(false); const [testTemplate, setTestTemplate] = useState(null); - const selectedProject = (projects as ProjectItem[]).find((p) => String(p.publicId) === selectedProjectId); + const selectedProject = Array.isArray(projects) ? (projects as ProjectItem[]).find((p) => String(p.publicId) === selectedProjectId) : undefined; const selectedProjectName = selectedProject?.projectName || 'Unknown Project'; // Master Data diff --git a/frontend/components/admin/ai/OcrSandboxPromptManager.tsx b/frontend/components/admin/ai/OcrSandboxPromptManager.tsx index 8e1c237c..991920c5 100644 --- a/frontend/components/admin/ai/OcrSandboxPromptManager.tsx +++ b/frontend/components/admin/ai/OcrSandboxPromptManager.tsx @@ -3,6 +3,7 @@ // - 2026-05-25: Created OcrSandboxPromptManager component for dynamic prompt editing, version control, and sandbox testing (ADR-029) // - 2026-05-25: Extracted inline strings to i18n keys via useTranslations() (Obs #1 fix) // - 2026-05-25: Refactored sandbox polling to useSandboxRun hook (Obs #2 fix) +// - 2026-05-26: เพิ่มการตรวจสอบ versionsQuery.data แบบทนทานเพื่อป้องกัน Error N.find is not a function ในกรณีที่ API ส่งข้อมูลแบบ wrapped object มา 'use client'; import React, { useState, useEffect } from 'react'; @@ -43,7 +44,12 @@ export default function OcrSandboxPromptManager() { deleteMutation, updateNoteMutation, } = useAiPrompts(promptType); - const versions = versionsQuery.data ?? []; + const versionsData = versionsQuery.data; + const versions = Array.isArray(versionsData) + ? versionsData + : (versionsData && typeof versionsData === 'object' && 'data' in versionsData && Array.isArray((versionsData as { data: unknown }).data)) + ? (versionsData as { data: AiPrompt[] }).data + : []; const activePrompt = versions.find((v) => v.isActive); const [templateText, setTemplateText] = useState(''); const [ocrFile, setOcrFile] = useState(null); diff --git a/frontend/lib/services/ai-prompts.service.ts b/frontend/lib/services/ai-prompts.service.ts index db7d8bd7..7b8a6cb7 100644 --- a/frontend/lib/services/ai-prompts.service.ts +++ b/frontend/lib/services/ai-prompts.service.ts @@ -1,6 +1,7 @@ // File: frontend/lib/services/ai-prompts.service.ts // Change Log // - 2026-05-25: Created aiPromptsService for prompt versioning and sandbox operations (ADR-029) +// - 2026-05-26: แก้ไขการ unwrap ข้อมูลจาก TransformInterceptor ที่ซ้อนกันหลายชั้นเพื่อป้องกันข้อผิดพลาด N.find is not a function import api from '../api/client'; import { AiPrompt } from '../../types/ai-prompts'; @@ -12,6 +13,17 @@ const extractData = (value: unknown): T => { return value as T; }; +/** + * ฟังก์ชันช่วย unwrap ข้อมูลจาก API Response ที่อาจจะถูก wrap ซ้อนกันหลายชั้นโดย TransformInterceptor + */ +const unwrapResponse = (value: unknown): T => { + const extracted = extractData(value); + if (extracted && typeof extracted === 'object' && 'data' in extracted) { + return (extracted as { data: T }).data; + } + return extracted; +}; + /** * Service สำหรับเรียก API ในการจัดการ AI prompt templates ทางฝั่งหน้าบ้าน */ @@ -21,7 +33,7 @@ export const aiPromptsService = { */ listVersions: async (promptType: string): Promise => { const { data } = await api.get(`/ai/prompts/${encodeURIComponent(promptType)}`); - return extractData(data); + return unwrapResponse(data); }, /** @@ -29,7 +41,7 @@ export const aiPromptsService = { */ createVersion: async (promptType: string, template: string): Promise => { const { data } = await api.post(`/ai/prompts/${encodeURIComponent(promptType)}`, { template }); - return extractData(data); + return unwrapResponse(data); }, /** @@ -39,7 +51,7 @@ export const aiPromptsService = { const { data } = await api.post( `/ai/prompts/${encodeURIComponent(promptType)}/${versionNumber}/activate` ); - return extractData(data); + return unwrapResponse(data); }, /** @@ -61,6 +73,7 @@ export const aiPromptsService = { `/ai/prompts/${encodeURIComponent(promptType)}/${versionNumber}/note`, { manualNote } ); - return extractData(data); + return unwrapResponse(data); }, }; +