This commit is contained in:
Your Name committed 2026-06-27 23:19:51 -04:00
1 parent 8f64d89f6f
commit b17e589577
18 files changed
+918 -517

No files matched your search

+87 -2
View File
@@ -38,6 +38,23 @@ const SECTION_TOOL_CONFIGS = [
},
required: ["sectionId"]
},
{
name: "update_editorial_tile",
sectionType: "EDITORIAL_TILE",
description: `Update an EDITORIAL_TILE section. Title, description, and buttonText are localized. imageUrl, imagePosition, buttonLink, backgroundColor, and textColor are plain strings. Example payload: { "sectionId": 12, "title": ${localizedStringExample}, "description": ${localizedStringExample}, "imageUrl": "https://..." }`,
properties: {
sectionId: { type: "number", description: "The numeric ID of the section to update" },
imageUrl: { type: "string", description: "Plain string main image URL" },
imagePosition: { type: "string", description: "Plain string image position ('left' or 'right')" },
title: { ...localizedStringSchema, description: `Localized title, for example ${localizedStringExample}` },
description: { ...localizedStringSchema, description: `Localized description, for example ${localizedStringExample}` },
buttonText: { ...localizedStringSchema, description: `Localized button text, for example ${localizedStringExample}` },
buttonLink: { type: "string", description: "Plain string button link URL" },
backgroundColor: { type: "string", description: "Plain string background color in hex or rgba" },
textColor: { type: "string", description: "Plain string text color in hex or rgba" }
},
required: ["sectionId"]
},
{
name: "update_rich_text",
sectionType: "RICH_TEXT",
@@ -150,6 +167,35 @@ const SECTION_TOOL_CONFIGS = [
subtitle: { ...localizedStringSchema, description: `Localized form subtitle, for example ${localizedStringExample}` }
},
required: ["sectionId"]
},
{
name: "update_editorial_carousel",
sectionType: "EDITORIAL_CAROUSEL",
description: `Update an EDITORIAL_CAROUSEL section. title, description, and slides are supported. Example payload: { "sectionId": 12, "title": ${localizedStringExample}, "description": ${localizedStringExample} }`,
properties: {
sectionId: { type: "number", description: "The numeric ID of the section to update" },
title: { ...localizedStringSchema, description: `Localized section title, for example ${localizedStringExample}` },
description: { ...localizedStringSchema, description: `Localized section description, for example ${localizedStringExample}` },
backgroundColor: { type: "string", description: "Plain string background color in hex or rgba" },
textColor: { type: "string", description: "Plain string text color in hex or rgba" },
slides: {
type: "array",
items: {
type: "object",
properties: {
title: localizedStringSchema,
description: localizedStringSchema,
category: localizedStringSchema,
imageUrl: { type: "string" },
buttonText: localizedStringSchema,
buttonLink: { type: "string" }
},
required: ["imageUrl"]
},
description: "Array of slides. IMPORTANT: Providing this will completely overwrite the existing slides array. Ensure you provide all slides."
}
},
required: ["sectionId"]
}
] as const;
@@ -201,6 +247,18 @@ function buildSectionUpdateResult(toolName: string, sectionType: string, updated
}
export const SECTION_TOOL_DEFINITIONS = [
{
name: "get_page_section",
description: "Get a specific page section by ID, returning its hydrated payload resolving linked content like categories or products. Optionally pass lang to control translation.",
inputSchema: {
type: "object",
properties: {
sectionId: { type: "number", description: "The numeric ID of the section to fetch" },
lang: { type: "string", description: "Optional language code (th, en, all)" }
},
required: ["sectionId"]
}
},
{
name: "add_page_section",
description: "Add a block section to a dynamic page layout. For the payload field, ensure it matches the section type. Examples: For TEXT, use { \"text\": \"...\" }. For RICH_TEXT, use { \"title\": \"...\", \"content\": \"...\" }.",
@@ -258,8 +316,21 @@ export const SECTION_TOOL_NAMES = new Set<string>(SECTION_TOOL_DEFINITIONS.map((
export async function handleSectionTool(api: AxiosInstance, name: string, params: any) {
if (!SECTION_TOOL_NAMES.has(name)) return null;
// Global validation: almost all section tools require sectionId, except add_page_section
if (name !== "add_page_section" && params.sectionId === undefined) {
throw new Error(`Validation Error: 'sectionId' is required for tool '${name}'`);
}
switch (name) {
case "get_page_section": {
const langParam = params.lang ? `?lang=${params.lang}` : "";
const res = await api.get(`/pages/sections/${params.sectionId}${langParam}`);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "add_page_section": {
if (params.pageId === undefined) {
throw new Error(`Validation Error: 'pageId' is required for tool '${name}'`);
}
const payload: any = {
type: params.type,
payload: params.payload,
@@ -278,6 +349,7 @@ export async function handleSectionTool(api: AxiosInstance, name: string, params
const res = await api.put(`/pages/sections/${params.sectionId}`, payload);
return buildSectionUpdateResult("update_page_section", res.data?.type ?? params.type ?? "UNKNOWN", res.data, payload);
}
case "update_editorial_tile":
case "update_hero_article":
case "update_rich_text":
case "update_scent_notes_breakdown":
@@ -285,7 +357,8 @@ export async function handleSectionTool(api: AxiosInstance, name: string, params
case "update_product_carousel":
case "update_accordion":
case "update_contact_info":
case "update_contact_form": {
case "update_contact_form":
case "update_editorial_carousel": {
const config = SECTION_TOOL_CONFIG_BY_NAME.get(name);
if (!config) {
throw new Error(`Unknown tool config for: ${name}`);
@@ -299,9 +372,21 @@ export async function handleSectionTool(api: AxiosInstance, name: string, params
payload.heartNotes = payload.middleNotes;
}
}
// Fetch existing section to merge payload so AI doesn't have to provide all required fields every time
let mergedPayload = payload;
try {
const existingRes = await api.get(`/pages/sections/${params.sectionId}?lang=all`);
if (existingRes.data && existingRes.data.payload) {
mergedPayload = { ...existingRes.data.payload, ...payload };
}
} catch (err) {
console.warn(`Could not fetch existing section ${params.sectionId} for merging, proceeding with partial payload.`);
}
const res = await api.put(`/pages/sections/${params.sectionId}`, {
type: config.sectionType,
...payload
payload: mergedPayload
});
return buildSectionUpdateResult(name, config.sectionType, res.data, payload);