리팩터링
CI / test (push) Successful in 5m38s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Successful in 1m12s

This commit is contained in:
2025-12-16 17:49:25 +09:00
parent 87cdc1868b
commit 7491dacba2
19 changed files with 1857 additions and 99 deletions
@@ -92,8 +92,9 @@ const EDITOR_PROJECT_PROPERTIES_PANEL_MESSAGES: Record<AppLocale, EditorProjectP
seoOgImageUrlAria: "OG/Twitter image URL",
seoOgImageUrlPlaceholder: "e.g. https://example.com/og-image.png",
seoCanonicalUrlLabel: "Canonical URL",
seoCanonicalUrlAria: "Canonical URL",
// Canonical 은 더 이상 <link rel=\"canonical\"> 로 쓰지 않고, 공유 URL(og:url) 설정 용도로만 사용한다.
seoCanonicalUrlLabel: "Share URL (og:url)",
seoCanonicalUrlAria: "Share URL (og:url)",
seoCanonicalUrlPlaceholder: "e.g. https://example.com/landing",
seoNoIndexAria: "Hide from search engines (noindex)",
@@ -145,8 +146,9 @@ const EDITOR_PROJECT_PROPERTIES_PANEL_MESSAGES: Record<AppLocale, EditorProjectP
seoOgImageUrlAria: "OG/Twitter 이미지 URL",
seoOgImageUrlPlaceholder: "예: https://example.com/og-image.png",
seoCanonicalUrlLabel: "Canonical URL",
seoCanonicalUrlAria: "Canonical URL",
// Canonical 은 더 이상 <link rel=\"canonical\"> 로 쓰지 않고, 공유 URL(og:url) 설정 용도로만 사용한다.
seoCanonicalUrlLabel: "공유 URL (og:url)",
seoCanonicalUrlAria: "공유 URL (og:url)",
seoCanonicalUrlPlaceholder: "예: https://example.com/landing",
seoNoIndexAria: "검색 엔진에 노출하지 않기 (noindex)",
+32
View File
@@ -19,6 +19,14 @@ export type ProjectsMessages = {
tableHeaderTitle: string;
tableHeaderSlug: string;
tableHeaderStatus: string;
tableHeaderStatusHelpLabel: string;
tableHeaderStatusHelpTooltip: string;
statusDraftLabel: string;
statusPublishedLabel: string;
statusArchivedLabel: string;
tableHeaderSubmissions: string;
tableHeaderSubmissionsHelpLabel: string;
tableHeaderSubmissionsHelpTooltip: string;
tableHeaderCreatedAt: string;
tableHeaderUpdatedAt: string;
tableHeaderActions: string;
@@ -60,6 +68,18 @@ const PROJECTS_MESSAGES: Record<AppLocale, ProjectsMessages> = {
tableHeaderTitle: "Title",
tableHeaderSlug: "Address (slug)",
tableHeaderStatus: "Status",
tableHeaderStatusHelpLabel: "Project status help",
tableHeaderStatusHelpTooltip:
"Draft: Public page is blocked (404) and form submissions are not accepted.\n" +
"Published: Public page is live and form submissions are accepted.\n" +
"Archived: Public page is live but new form submissions are blocked.",
statusDraftLabel: "Draft (blocked)",
statusPublishedLabel: "Published (live)",
statusArchivedLabel: "Archived (read-only)",
tableHeaderSubmissions: "Submissions (today/total)",
tableHeaderSubmissionsHelpLabel: "Submission counts help",
tableHeaderSubmissionsHelpTooltip:
"Shows form submission counts as [icon] today/total (today's submissions / all submissions).",
tableHeaderCreatedAt: "Created at",
tableHeaderUpdatedAt: "Updated at",
tableHeaderActions: "Actions",
@@ -100,6 +120,18 @@ const PROJECTS_MESSAGES: Record<AppLocale, ProjectsMessages> = {
tableHeaderTitle: "제목",
tableHeaderSlug: "주소(slug)",
tableHeaderStatus: "상태",
tableHeaderStatusHelpLabel: "프로젝트 상태 설명",
tableHeaderStatusHelpTooltip:
"차단: 퍼블릭 페이지가 404로 차단되고 폼 제출을 받지 않습니다.\n" +
"운영 중: 퍼블릭 페이지에 노출되고 폼 제출을 정상적으로 받습니다.\n" +
"제출 중단(오픈): 퍼블릭 페이지는 열리지만 새 폼 제출은 차단됩니다.",
statusDraftLabel: "차단",
statusPublishedLabel: "운영 중",
statusArchivedLabel: "제출 중단(오픈)",
tableHeaderSubmissions: "제출 내역 (오늘/전체)",
tableHeaderSubmissionsHelpLabel: "제출 내역 수치 설명",
tableHeaderSubmissionsHelpTooltip:
"아이콘 옆 숫자는 '오늘 제출 수 / 전체 누적 제출 수'를 의미합니다.",
tableHeaderCreatedAt: "생성일",
tableHeaderUpdatedAt: "수정일",
tableHeaderActions: "액션",
+27 -2
View File
@@ -1,9 +1,11 @@
import type { Block } from "@/features/editor/state/editorStore";
import type { Block, ProjectConfig } from "@/features/editor/state/editorStore";
interface PublicProjectSnapshot {
slug: string;
title: string;
contentJson: Block[];
status: string;
blocks: Block[];
projectConfig: ProjectConfig | null;
}
function getStore(): Map<string, PublicProjectSnapshot> {
@@ -23,3 +25,26 @@ export function getCachedPublicProject(slug: string): PublicProjectSnapshot | nu
const store = getStore();
return store.get(slug) ?? null;
}
export function parseProjectContentJson(rawContent: any): {
blocks: Block[];
projectConfig: ProjectConfig | null;
} {
let blocks: Block[] = [];
let projectConfig: ProjectConfig | null = null;
if (Array.isArray(rawContent)) {
blocks = rawContent as Block[];
} else if (rawContent && typeof rawContent === "object") {
const maybeBlocks = (rawContent as any).blocks;
if (Array.isArray(maybeBlocks)) {
blocks = maybeBlocks as Block[];
const maybeConfig = (rawContent as any).projectConfig;
if (maybeConfig && typeof maybeConfig === "object") {
projectConfig = maybeConfig as ProjectConfig;
}
}
}
return { blocks, projectConfig };
}