+
+
+
+
Principles
+
기록을 운영하는 원칙
+
+
+ {principles.map((principle, index) => (
+ -
+ {String(index + 1).padStart(2, "0")}
+
+
{principle.title}
+
{principle.description}
+
+
+ ))}
+
+
+
+
+ Topics
+ 주요 관심 주제
+
+ {topics.map((topic) => (
+ - {topic}
+ ))}
+
+
+
+ );
+}
diff --git a/src/features/tech-log/presentation/public/pages/project-activity-page.tsx b/src/features/tech-log/presentation/public/pages/project-activity-page.tsx
new file mode 100644
index 0000000..421964f
--- /dev/null
+++ b/src/features/tech-log/presentation/public/pages/project-activity-page.tsx
@@ -0,0 +1,44 @@
+import { Link } from "react-router-dom";
+
+import { TECH_LOG_FEATURE_ID } from "../../../application/tech-log-feature-input.ts";
+import { useApplication } from "../../../../../presentation/providers/application-provider.tsx";
+import {
+ RegisteredNotFoundRoute,
+ useRouteInput,
+} from "../../../../../presentation/routes/route-input.tsx";
+import { ProjectPageHeader } from "../components/project-page-header.tsx";
+
+export function ProjectActivityPage() {
+ const { params } = useRouteInput<"TECH_LOG_PROJECT_ACTIVITY">();
+ const slug = typeof params.slug === "string" ? params.slug : "";
+ const { publicContent } = useApplication().features.get(TECH_LOG_FEATURE_ID);
+ const project = publicContent.getProject(slug);
+
+ if (!project) return
+
+
+ Thesis
+ 프로젝트가 지키는 기준
+ {project.thesis}
+
+
+
+
- 단계
+ - {project.stage}
+
+
+
- 현재 목표
+ - {project.currentGoal}
+
+
+
- 다음 작업
+ - {project.nextStep}
+
+
+
+
+
+
+ {records.length}
+ 공개 기록
+
+
+ {decisions.length}
+ 설계 결정
+
+
+ {activity.length}
+ 활동 기록
+
+
+
+
+ 주요 주제
+
+ {project.topics.map((topic) => (
+ - {topic}
+ ))}
+
+
+
+ );
+}
diff --git a/src/features/tech-log/presentation/public/pages/project-records-page.tsx b/src/features/tech-log/presentation/public/pages/project-records-page.tsx
new file mode 100644
index 0000000..c3cec6e
--- /dev/null
+++ b/src/features/tech-log/presentation/public/pages/project-records-page.tsx
@@ -0,0 +1,29 @@
+import { TECH_LOG_FEATURE_ID } from "../../../application/tech-log-feature-input.ts";
+import { useApplication } from "../../../../../presentation/providers/application-provider.tsx";
+import {
+ RegisteredNotFoundRoute,
+ useRouteInput,
+} from "../../../../../presentation/routes/route-input.tsx";
+import { ProjectPageHeader } from "../components/project-page-header.tsx";
+import { PublicRecordList } from "../components/public-record-list.tsx";
+
+export function ProjectRecordsPage() {
+ const { params } = useRouteInput<"TECH_LOG_PROJECT_RECORDS">();
+ const slug = typeof params.slug === "string" ? params.slug : "";
+ const { publicContent } = useApplication().features.get(TECH_LOG_FEATURE_ID);
+ const project = publicContent.getProject(slug);
+
+ if (!project) return
+
+
+ {projects.map((project, index) => (
+ -
+
+ {String(index + 1).padStart(2, "0")}
+
+
+
{project.title}
+ {project.stage}
+
+
{project.summary}
+
+
+
- 현재 목표
+ - {project.currentGoal}
+
+
+
- 다음 작업
+ - {project.nextStep}
+
+
+
+ ↗
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/features/tech-log/presentation/public/pages/public-not-found-page.tsx b/src/features/tech-log/presentation/public/pages/public-not-found-page.tsx
new file mode 100644
index 0000000..b0c1a4c
--- /dev/null
+++ b/src/features/tech-log/presentation/public/pages/public-not-found-page.tsx
@@ -0,0 +1,53 @@
+import type { CSSProperties } from "react";
+
+const styles = {
+ error: {
+ fontFamily:
+ 'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',
+ height: "100vh",
+ textAlign: "center",
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ desc: {
+ display: "inline-block",
+ },
+ h1: {
+ display: "inline-block",
+ margin: "0 20px 0 0",
+ padding: "0 23px 0 0",
+ fontSize: 24,
+ fontWeight: 500,
+ verticalAlign: "top",
+ lineHeight: "49px",
+ },
+ h2: {
+ fontSize: 14,
+ fontWeight: 400,
+ lineHeight: "49px",
+ margin: 0,
+ },
+} as const satisfies Record