From e16f8298abc663d42ba1c2b59c2a00ca3fec0a73 Mon Sep 17 00:00:00 2001 From: Jaybe Date: Thu, 27 Nov 2025 11:20:38 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20FormBlock=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=2015.1=20SEO/head=20?= =?UTF-8?q?=EB=A9=94=ED=83=80=20=EA=B4=80=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/export/route.ts | 77 ++++++++++++- src/app/editor/forms/FormControllerPanel.tsx | 85 --------------- .../editor/panels/ProjectPropertiesPanel.tsx | 59 ++++++++++ src/features/editor/state/editorStore.ts | 12 +++ src/features/editor/utils/formHelpers.ts | 26 +---- tests/api/export.spec.ts | 86 ++++++++++++++- .../unit/ProjectPropertiesPanel.seo.spec.tsx | 102 ++++++++++++++++++ .../TextListFormBackgroundControls.spec.tsx | 31 ------ tests/unit/formHelpers.spec.ts | 10 +- 9 files changed, 341 insertions(+), 147 deletions(-) create mode 100644 tests/unit/ProjectPropertiesPanel.seo.spec.tsx diff --git a/src/app/api/export/route.ts b/src/app/api/export/route.ts index 0a9106c..9ecc524 100644 --- a/src/app/api/export/route.ts +++ b/src/app/api/export/route.ts @@ -52,7 +52,9 @@ const escapeHtml = (value: string): string => const escapeAttr = (value: string): string => escapeHtml(value); export const buildStaticHtml = (blocks: Block[], projectConfig?: ProjectConfig): string => { - const pageTitleRaw = (projectConfig?.title ?? "").trim() || "Page Builder Export"; + const baseTitleRaw = (projectConfig?.title ?? "").trim() || "Page Builder Export"; + const seoTitleRaw = (projectConfig?.seoTitle ?? "").trim(); + const pageTitleRaw = seoTitleRaw || baseTitleRaw; const pageTitle = escapeHtml(pageTitleRaw); const headExtraRaw = (projectConfig?.headHtml ?? "").trim(); @@ -84,6 +86,77 @@ export const buildStaticHtml = (blocks: Block[], projectConfig?: ProjectConfig): const trackingRaw = (projectConfig?.trackingScript ?? "").trim(); const trackingHtml = trackingRaw ? `\n${trackingRaw}\n` : ""; + const seoDescriptionRaw = (projectConfig?.seoDescription ?? "").trim(); + const seoOgImageRaw = (projectConfig?.seoOgImageUrl ?? "").trim(); + const seoCanonicalRaw = (projectConfig?.seoCanonicalUrl ?? "").trim(); + const seoNoIndex = projectConfig?.seoNoIndex === true; + + const seoHeadParts: string[] = []; + + if (seoDescriptionRaw) { + seoHeadParts.push( + ` `, + ); + } + + if (pageTitleRaw) { + seoHeadParts.push( + ` `, + ); + } + if (seoDescriptionRaw) { + seoHeadParts.push( + ` `, + ); + } + seoHeadParts.push(` `); + + if (seoOgImageRaw) { + seoHeadParts.push( + ` `, + ); + } + + if (seoCanonicalRaw) { + seoHeadParts.push( + ` `, + ); + } + + // Twitter 카드 메타 태그 + seoHeadParts.push( + ` `, + ); + if (pageTitleRaw) { + seoHeadParts.push( + ` `, + ); + } + if (seoDescriptionRaw) { + seoHeadParts.push( + ` `, + ); + } + if (seoOgImageRaw) { + seoHeadParts.push( + ` `, + ); + } + + if (seoCanonicalRaw) { + seoHeadParts.push( + ` `, + ); + } + + if (seoNoIndex) { + seoHeadParts.push( + ` `, + ); + } + + const seoHeadHtml = seoHeadParts.length > 0 ? `\n${seoHeadParts.join("\n")}` : ""; + const sectionBlocks = blocks.filter((b) => b.type === "section"); const rootBlocks = blocks.filter((b) => !b.sectionId && b.type !== "section"); @@ -515,7 +588,7 @@ export const buildStaticHtml = (blocks: Block[], projectConfig?: ProjectConfig): ${pageTitle} - ${headExtra} + ${seoHeadHtml}${headExtra}
diff --git a/src/app/editor/forms/FormControllerPanel.tsx b/src/app/editor/forms/FormControllerPanel.tsx index 811a67b..a3d5996 100644 --- a/src/app/editor/forms/FormControllerPanel.tsx +++ b/src/app/editor/forms/FormControllerPanel.tsx @@ -1,8 +1,6 @@ "use client"; import type { Block, ButtonBlockProps, FormBlockProps } from "@/features/editor/state/editorStore"; -import { NumericPropertyControl } from "@/features/editor/components/NumericPropertyControl"; -import { ColorPickerField, TEXT_COLOR_PALETTE } from "@/features/editor/components/ColorPickerField"; interface FormControllerPanelProps { block: Block; // type === "form" @@ -131,89 +129,6 @@ export function FormControllerPanel({ block, blocks, selectedBlockId, updateBloc )} -
-

폼 레이아웃

- - {(formProps.formWidthMode ?? "auto") === "fixed" && ( - - updateBlock(selectedBlockId, { - formWidthPx: v, - } as any) - } - /> - )} - - updateBlock(selectedBlockId, { - marginYPx: v, - } as any) - } - /> -
- -
-

폼 스타일

- { - updateBlock(selectedBlockId, { - backgroundColorCustom: hex, - } as any); - }} - palette={TEXT_COLOR_PALETTE} - onPaletteSelect={(item) => { - updateBlock(selectedBlockId, { - backgroundColorCustom: item.color, - } as any); - }} - /> -
-

폼 메시지

+
+

SEO / 메타

+ + + +