feat: FormBlock 컨트롤러 정리 및 15.1 SEO/head 메타 관리 추가
CI / test (push) Failing after 7m19s
CI / pr_and_merge (push) Has been skipped

This commit is contained in:
2025-11-27 11:20:38 +09:00
parent 48e13d2a75
commit e16f8298ab
9 changed files with 341 additions and 147 deletions
@@ -104,6 +104,65 @@ export function ProjectPropertiesPanel() {
/>
</div>
<div className="space-y-2 border-t border-slate-800 pt-3">
<h4 className="text-[11px] font-semibold text-slate-200">SEO / </h4>
<label className="flex flex-col gap-1">
<span className="text-slate-400">SEO </span>
<input
className="w-full rounded border border-slate-700 bg-slate-900 px-2 py-1 text-xs outline-none focus:border-sky-500"
aria-label="SEO 타이틀"
placeholder={projectConfig.title || "페이지 제목"}
value={projectConfig.seoTitle ?? ""}
onChange={(e) => updateProjectConfig({ seoTitle: e.target.value })}
/>
</label>
<label className="flex flex-col gap-1">
<span className="text-slate-400"> </span>
<textarea
className="w-full rounded border border-slate-700 bg-slate-900 px-2 py-1 text-[11px] outline-none focus:border-sky-500 min-h-[56px]"
aria-label="메타 디스크립션"
placeholder="검색엔진 및 SNS 공유에 노출될 페이지 설명을 입력하세요."
value={projectConfig.seoDescription ?? ""}
onChange={(e) => updateProjectConfig({ seoDescription: e.target.value })}
/>
</label>
<label className="flex flex-col gap-1">
<span className="text-slate-400">OG/Twitter URL</span>
<input
className="w-full rounded border border-slate-700 bg-slate-900 px-2 py-1 text-xs outline-none focus:border-sky-500"
aria-label="OG/Twitter 이미지 URL"
placeholder="예: https://example.com/og-image.png"
value={projectConfig.seoOgImageUrl ?? ""}
onChange={(e) => updateProjectConfig({ seoOgImageUrl: e.target.value })}
/>
</label>
<label className="flex flex-col gap-1">
<span className="text-slate-400">Canonical URL</span>
<input
className="w-full rounded border border-slate-700 bg-slate-900 px-2 py-1 text-xs outline-none focus:border-sky-500"
aria-label="Canonical URL"
placeholder="예: https://example.com/landing"
value={projectConfig.seoCanonicalUrl ?? ""}
onChange={(e) => updateProjectConfig({ seoCanonicalUrl: e.target.value })}
/>
</label>
<label className="flex items-center gap-2 text-[11px] text-slate-300">
<input
type="checkbox"
className="h-3 w-3 rounded border-slate-600 bg-slate-900"
aria-label="검색 엔진에 노출하지 않기 (noindex)"
checked={Boolean(projectConfig.seoNoIndex)}
onChange={(e) => updateProjectConfig({ seoNoIndex: e.target.checked })}
/>
<span> (noindex)</span>
</label>
</div>
<div className="space-y-1">
<label className="flex flex-col gap-1">
<span className="text-slate-400"> head HTML</span>