diff --git a/src/app/api/forms/submit/route.ts b/src/app/api/forms/submit/route.ts new file mode 100644 index 0000000..b0bf126 --- /dev/null +++ b/src/app/api/forms/submit/route.ts @@ -0,0 +1,14 @@ +import { NextResponse } from "next/server"; + +export async function POST(req: Request) { + const formData = await req.formData(); + + const name = formData.get("name"); + const email = formData.get("email"); + const message = formData.get("message"); + + // TODO: 이곳에서 DB 저장이나 이메일 전송 등 실제 처리를 수행한다. + console.log("[forms/submit]", { name, email, message }); + + return NextResponse.json({ ok: true }); +} diff --git a/src/app/editor/page.tsx b/src/app/editor/page.tsx index d45953b..9ec01a0 100644 --- a/src/app/editor/page.tsx +++ b/src/app/editor/page.tsx @@ -31,7 +31,14 @@ import type { SectionBlockProps, DividerBlockProps, ListBlockProps, + FormBlockProps, } from "@/features/editor/state/editorStore"; +import { ButtonPropertiesPanel } from "./panels/ButtonPropertiesPanel"; +import { TextPropertiesPanel } from "./panels/TextPropertiesPanel"; +import { ListPropertiesPanel } from "./panels/ListPropertiesPanel"; +import { DividerPropertiesPanel } from "./panels/DividerPropertiesPanel"; +import { ImagePropertiesPanel } from "./panels/ImagePropertiesPanel"; +import { SectionPropertiesPanel } from "./panels/SectionPropertiesPanel"; export default function EditorPage() { const blocks = useEditorStore((state) => state.blocks); @@ -42,6 +49,7 @@ export default function EditorPage() { const addDividerBlock = useEditorStore((state) => state.addDividerBlock); const addListBlock = useEditorStore((state) => state.addListBlock); const addSectionBlock = useEditorStore((state) => state.addSectionBlock); + const addFormBlock = useEditorStore((state) => state.addFormBlock); const addHeroTemplateSection = useEditorStore((state) => state.addHeroTemplateSection); const addFeaturesTemplateSection = useEditorStore((state) => state.addFeaturesTemplateSection); const addCtaTemplateSection = useEditorStore((state) => state.addCtaTemplateSection); @@ -75,6 +83,9 @@ export default function EditorPage() { const [loadSlug, setLoadSlug] = useState(""); const [projectMessage, setProjectMessage] = useState(""); + const [menuOpen, setMenuOpen] = useState(false); + const [activeModal, setActiveModal] = useState<"project" | "json" | null>(null); + const sensors = useSensors(useSensor(PointerSensor)); const startEditing = (id: string, initialText: string) => { @@ -133,11 +144,22 @@ export default function EditorPage() { const handleSaveProject = async () => { const slug = projectSlug.trim(); if (!slug) { - setProjectMessage("슬러그를 입력해 주세요."); + setProjectMessage("프로젝트 주소를 입력해 주세요."); return; } try { + // 1) 로컬스토리지에 현재 상태 저장 (슬러그 기준) + if (typeof window !== "undefined") { + const localKey = `pb:project:${slug}`; + const payload = { + title: projectTitle.trim() || "제목 없음", + contentJson: blocks, + }; + window.localStorage.setItem(localKey, JSON.stringify(payload)); + } + + // 2) 서버에도 저장 (백업/공유용) const response = await fetch("/api/projects", { method: "POST", headers: { @@ -166,11 +188,30 @@ export default function EditorPage() { const handleLoadProject = async () => { const slug = loadSlug.trim() || projectSlug.trim(); if (!slug) { - setProjectMessage("불러올 슬러그를 입력해 주세요."); + setProjectMessage("불러올 주소를 입력해 주세요."); return; } try { + // 1) 로컬스토리지에서 먼저 조회 + if (typeof window !== "undefined") { + const localKey = `pb:project:${slug}`; + const raw = window.localStorage.getItem(localKey); + if (raw) { + try { + const parsed = JSON.parse(raw); + if (parsed && Array.isArray(parsed.contentJson)) { + replaceBlocks(parsed.contentJson as any); + setProjectMessage(`로컬에서 프로젝트를 불러왔습니다: ${slug}`); + return; + } + } catch { + // 로컬 파싱 에러는 무시하고 서버 시도 + } + } + } + + // 2) 로컬에 없으면 서버에서 조회 const response = await fetch(`/api/projects/${slug}`); if (!response.ok) { setProjectMessage("프로젝트를 불러오지 못했습니다."); @@ -468,6 +509,7 @@ export default function EditorPage() { selectBlock={selectBlock} allBlocks={blocks} renderBlocks={renderBlocks} + updateBlock={updateBlock} /> ); @@ -475,17 +517,53 @@ export default function EditorPage() { return (
-
+

Page Editor

빌더 MVP용 에디터 페이지 골격

- - 프리뷰 열기 - +
+ + 프리뷰 열기 + +
+ + {menuOpen && ( +
+ + +
+ )} +
+