From 750ea9caf5bc8ce380ea2705d87406c65e19a606 Mon Sep 17 00:00:00 2001 From: Driedoutjerky Date: Fri, 29 May 2026 15:54:20 +0900 Subject: [PATCH 1/8] init: mockData.js --- src/api/mockData.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/api/mockData.js diff --git a/src/api/mockData.js b/src/api/mockData.js new file mode 100644 index 0000000..348fe52 --- /dev/null +++ b/src/api/mockData.js @@ -0,0 +1,32 @@ +export const mockPosts = [ + { + post_id: 1, + user_id: 1, + title: "The First Crack", + content: + "This morning, I found a tiny crack on the surface of the egg. Is it a sign that my will is starting to take shape?", + image_url: null, + tag: "growth", + visibility: "public", + word_count: 24, + will_reward: 12, + created_at: "2026-05-29", + updated_at: "2026-05-29", + }, + { + post_id: 2, + user_id: 1, + title: "Study Day", + content: + "I studied React today. Hooks are still confusing, but I am starting to understand how state controls the page.", + image_url: null, + tag: "study", + visibility: "private", + word_count: 19, + will_reward: 10, + created_at: "2026-05-29", + updated_at: "2026-05-29", + }, +]; + + From 494e6b3c636e260bd3210bb998ff7beaeb52ed3a Mon Sep 17 00:00:00 2001 From: Driedoutjerky Date: Fri, 29 May 2026 16:49:30 +0900 Subject: [PATCH 2/8] feat:MockcreatePost --- src/App.jsx | 2 +- src/api/postsApi.js | 49 ++++++++++++++++++++++++++++++ src/hooks/usePosts.js | 35 ++++++++++++++++++++++ src/pages/WritePostPage.jsx | 60 ++++++++++++++++++++++++++++++++++--- 4 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 src/api/postsApi.js create mode 100644 src/hooks/usePosts.js diff --git a/src/App.jsx b/src/App.jsx index 6d5448c..7cd589c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -15,7 +15,7 @@ import ShopPage from "./pages/ShopPage.jsx"; import MemoryArchivePage from "./pages/MemoryArchivePage.jsx"; function App() { - return ; + return ; /* return ( diff --git a/src/api/postsApi.js b/src/api/postsApi.js new file mode 100644 index 0000000..52c7eb3 --- /dev/null +++ b/src/api/postsApi.js @@ -0,0 +1,49 @@ +/* NOTE: THIS FILE WILL BE REPLACED WITH FETCH() CALLS. + LOGICS SHOULD BE IMPLEMENTED IN BACKEND. */ + +import { mockPosts } from "./mockData"; + +function countWords(text) { + const trimmed = text.trim(); + + if (!trimmed) { + return 0; + } + + return trimmed.split(/\s+/).length; +} + +/*(Will Reward per post) = wordCount / 10 */ +function calculateWillReward(wordCount) { + return Math.max(1, Math.floor(wordCount / 10)); +} + +export async function getAllPosts() { + return mockPosts; +} + +export async function getPostById(postId) { + return mockPosts.find((post) => post.post_id === Number(postId)); +} + +export async function createPost(postData) { + const wordCount = countWords(postData.content); + + const newPost = { + post_id: Date.now(), + user_id: 1, + title: postData.title, + content: postData.content, + image_url: postData.image_url || null, + tag: postData.tag || "reflection", + visibility: postData.visibility, + word_count: wordCount, + will_reward: calculateWillReward(wordCount), + created_at: new Date().toISOString().slice(0, 10), + updated_at: new Date().toISOString().slice(0, 10), + }; + + mockPosts.unshift(newPost); + + return newPost; +} \ No newline at end of file diff --git a/src/hooks/usePosts.js b/src/hooks/usePosts.js new file mode 100644 index 0000000..9197396 --- /dev/null +++ b/src/hooks/usePosts.js @@ -0,0 +1,35 @@ +import { useEffect, useState } from "react"; +import { createPost, getAllPosts } from "../api/postsApi"; + +export function usePosts() { + const [posts, setPosts] = useState([]); + const [loading, setLoading] = useState(true); + + async function loadPosts() { + setLoading(true); + + const data = await getAllPosts(); + + setPosts(data); + setLoading(false); + } + + async function addPost(postData) { + const newPost = await createPost(postData); + + setPosts((prevPosts) => [newPost, ...prevPosts]); + + return newPost; + } + + useEffect(() => { + loadPosts(); + }, []); + + return { + posts, + loading, + addPost, + reloadPosts: loadPosts, + }; +} \ No newline at end of file diff --git a/src/pages/WritePostPage.jsx b/src/pages/WritePostPage.jsx index 721da18..084bc54 100644 --- a/src/pages/WritePostPage.jsx +++ b/src/pages/WritePostPage.jsx @@ -1,8 +1,46 @@ import { useState } from "react"; +import { createPost } from "../api/postsApi"; import "./WritePostPage.css"; function WritePostPage() { + + const [title, setTitle] = useState(""); + const [content, setContent] = useState(""); + const [tag, setTag] = useState("reflection"); + const [imageUrl, setImageUrl] = useState(""); const [visibility, setVisibility] = useState("private"); + + {/*NOTE: LOGICS SHOULD BE IMPLEMENTED IN BACKEND. THESE ARE TEMPORARY PLACEHOLDERS */} + const wordCount = content.trim() ? content.trim().split(/\s+/).length : 0; + const estimatedWill = Math.max(1, Math.floor(wordCount / 10)); + + async function handleSubmit(event) { + event.preventDefault(); /*Stop page from refreshing in form submit*/ + + if (!title.trim() || !content.trim()) { + alert("Please write both title and content."); + return; + } + + const newPost = await createPost({ + title, + content, + tag, + image_url: imageUrl || null, + visibility, + }); + + console.log("Created post:", newPost); + + alert("Post created!"); + + setTitle(""); + setContent(""); + setTag("reflection"); + setImageUrl(""); + setVisibility("private"); + } + return (
@@ -29,7 +67,7 @@ function WritePostPage() {

Take a moment to reflect and write.

-
+
@@ -47,6 +87,8 @@ function WritePostPage() { id="post-body" className="write-body-input" placeholder="Start typing here..." + value={content} + onChange={(event) => setContent(event.target.value)} /> @@ -57,6 +99,16 @@ function WritePostPage() {
+ +
Post visibility @@ -95,7 +147,7 @@ function WritePostPage() {
-
@@ -108,12 +160,12 @@ function WritePostPage() {
Word Count - ↝ 0 + ↝ {wordCount}
Estimated Will - ↝ 0 + ↝ {estimatedWill}
From dac5591e3645fef41f2de299fdebc542f4b00eca Mon Sep 17 00:00:00 2001 From: Driedoutjerky Date: Fri, 29 May 2026 17:03:42 +0900 Subject: [PATCH 3/8] feat: connect write post and archive with mock postsApi --- src/App.jsx | 2 +- src/api/postsApi.js | 73 ++++++- src/pages/MemoryArchivePage.jsx | 343 ++++---------------------------- 3 files changed, 106 insertions(+), 312 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 7cd589c..6e4fec0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -15,7 +15,7 @@ import ShopPage from "./pages/ShopPage.jsx"; import MemoryArchivePage from "./pages/MemoryArchivePage.jsx"; function App() { - return ; + return ; /* return ( diff --git a/src/api/postsApi.js b/src/api/postsApi.js index 52c7eb3..8e0ade4 100644 --- a/src/api/postsApi.js +++ b/src/api/postsApi.js @@ -1,7 +1,56 @@ -/* NOTE: THIS FILE WILL BE REPLACED WITH FETCH() CALLS. - LOGICS SHOULD BE IMPLEMENTED IN BACKEND. */ +const STORAGE_KEY = "memory_egg_posts"; + +const defaultPosts = [ + { + post_id: 1, + user_id: 1, + title: "The First Crack", + content: + "This morning, I found a tiny crack on the surface of the egg. Is it a sign that my will is starting to take shape?", + image_url: null, + tag: "growth", + visibility: "public", + word_count: 24, + will_reward: 12, + created_at: "2026-05-29", + updated_at: "2026-05-29", + }, + { + post_id: 2, + user_id: 1, + title: "Study Day", + content: + "I studied React today. Hooks are still confusing, but I am starting to understand how state controls the page.", + image_url: null, + tag: "study", + visibility: "private", + word_count: 19, + will_reward: 10, + created_at: "2026-05-29", + updated_at: "2026-05-29", + }, +]; + + +/* localStorage for testing WritePostPage and MemoryARchivePage interaction */ + +function loadPostsFromStorage() { + const savedPosts = localStorage.getItem(STORAGE_KEY); + + if (!savedPosts) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultPosts)); + return defaultPosts; + } + + return JSON.parse(savedPosts); +} + +function savePostsToStorage(posts) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(posts)); +} + + -import { mockPosts } from "./mockData"; function countWords(text) { const trimmed = text.trim(); @@ -13,20 +62,22 @@ function countWords(text) { return trimmed.split(/\s+/).length; } -/*(Will Reward per post) = wordCount / 10 */ function calculateWillReward(wordCount) { return Math.max(1, Math.floor(wordCount / 10)); } export async function getAllPosts() { - return mockPosts; + return loadPostsFromStorage(); } export async function getPostById(postId) { - return mockPosts.find((post) => post.post_id === Number(postId)); + const posts = loadPostsFromStorage(); + + return posts.find((post) => post.post_id === Number(postId)); } export async function createPost(postData) { + const posts = loadPostsFromStorage(); const wordCount = countWords(postData.content); const newPost = { @@ -35,15 +86,17 @@ export async function createPost(postData) { title: postData.title, content: postData.content, image_url: postData.image_url || null, - tag: postData.tag || "reflection", + tag: postData.tag, visibility: postData.visibility, word_count: wordCount, will_reward: calculateWillReward(wordCount), - created_at: new Date().toISOString().slice(0, 10), - updated_at: new Date().toISOString().slice(0, 10), + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), }; - mockPosts.unshift(newPost); + const updatedPosts = [newPost, ...posts]; + + savePostsToStorage(updatedPosts); return newPost; } \ No newline at end of file diff --git a/src/pages/MemoryArchivePage.jsx b/src/pages/MemoryArchivePage.jsx index 03ba401..ab42b34 100644 --- a/src/pages/MemoryArchivePage.jsx +++ b/src/pages/MemoryArchivePage.jsx @@ -1,304 +1,7 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { getAllPosts } from "../api/postsApi"; import "./MemoryArchivePage.css"; -/* This is just example of archivePosts. Also tests if post list scroll works finely. */ -const archivePosts = [ - { - post_id: 1, - user_id: 1, - title: "Nov 2", - content: - "I'm studying Web Programming. This is so fun as I could create my imagination into something real.", - image_url: null, - tag: "study", - visibility: "private", - word_count: 128, - will_reward: 12, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 2, - user_id: 1, - title: "The Storm of chips", - content: - "I love potato chips. A bottle of Coca-cola goes well with this ngl.", - image_url: - "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=400&q=80", - tag: "food", - visibility: "public", - word_count: 286, - will_reward: 18, - created_at: "2025-11-08", - updated_at: "2025-11-08", - }, - { - post_id: 3, - user_id: 1, - title: "The First Crack", - content: - "This morning, I found a tiny crack on the surface of the egg. Is it a sign that my will is starting to take shape? I feel a swell of emotion in my heart. It feels real now. The weight of existence is shifting into something new.", - image_url: null, - tag: "growth", - visibility: "public", - word_count: 342, - will_reward: 50, - created_at: "2025-11-24", - updated_at: "2025-11-24", - }, - { - post_id: 4, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 5, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 6, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 7, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 8, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 9, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 10, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 11, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 12, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 13, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 14, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 15, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 16, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 17, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 18, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 19, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 20, - user_id: 1, - title: "Example", - content: - "Welcome to the Nacimiento. This is driedoutjerky who's working on the frontend. It's my first time using React and actually feels quite similar to the html. I need to make css anyway.", - image_url: null, - tag: "reflection", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: "2025-11-02", - updated_at: "2025-11-02", - }, - { - post_id: 21, - user_id: 1, - title: "Unknown Memory", - content: - "A memory waiting to be found. The page is still blank, but something about it feels familiar.", - image_url: null, - tag: "mystery", - visibility: "private", - word_count: 0, - will_reward: 0, - created_at: null, - updated_at: null, - }, -]; function formatPostDate(dateString) { if (!dateString) { @@ -387,7 +90,45 @@ function getTagClassName(tag) { function MemoryArchivePage() { - const [selectedPost, setSelectedPost] = useState(archivePosts[2]); + const [archivePosts, setArchivePosts] = useState([]); + const [selectedPost, setSelectedPost] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function loadArchivePosts() { + const posts = await getAllPosts(); + + setArchivePosts(posts); + setSelectedPost(posts[0] || null); + setLoading(false); + } + + loadArchivePosts(); + }, []); + + if (loading) { + return ( +
+

Loading memories...

+
+ ); + } + + if (archivePosts.length === 0 || !selectedPost) { + return ( +
+
+
Nacimiento
+
+ +
+

No memories yet

+

Write your first notebook post to fill the archive.

+ Write a post +
+
+ ); + } return (
@@ -428,7 +169,7 @@ function MemoryArchivePage() {

Archive Progress

-

20 memories found

+

{archivePosts.length} memories found

From 22cebfbd50313e6134e9d59b805ef08c3805e98f Mon Sep 17 00:00:00 2001 From: Driedoutjerky Date: Fri, 29 May 2026 17:18:52 +0900 Subject: [PATCH 4/8] feat:can view pages from archive now --- package-lock.json | 60 +++++++++++++- package.json | 3 +- src/App.jsx | 28 +++---- src/pages/MemoryArchivePage.jsx | 2 +- src/pages/ViewPostPage.jsx | 133 +++++++++++--------------------- 5 files changed, 116 insertions(+), 110 deletions(-) diff --git a/package-lock.json b/package-lock.json index aae39d6..3645bc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "react-router-dom": "^7.16.0" }, "devDependencies": { "@eslint/js": "^10.0.1", @@ -1068,6 +1069,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2141,6 +2155,44 @@ "react": "^19.2.6" } }, + "node_modules/react-router": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.16.0.tgz", + "integrity": "sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.16.0.tgz", + "integrity": "sha512-kMUAbimWB5FVbF4Bce4bJsiKJWLIUHq/mEG8+CFDnCSgltptBiG5nguducmsJeGKytlCvQud9Qhzpn49iduTlA==", + "license": "MIT", + "dependencies": { + "react-router": "7.16.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/rolldown": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", @@ -2191,6 +2243,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index c67d618..07ff890 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "react-router-dom": "^7.16.0" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/src/App.jsx b/src/App.jsx index 6e4fec0..bebc5fc 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,6 +2,7 @@ //
tests whether centered layout works. //
tests whether reusable panel and vertical spacing classes work. //

tests title style +import { BrowserRouter, Routes, Route } from "react-router-dom"; import OpeningPage from "./pages/OpeningPage.jsx"; import RegisterPage from "./pages/RegisterPage.jsx"; @@ -15,25 +16,16 @@ import ShopPage from "./pages/ShopPage.jsx"; import MemoryArchivePage from "./pages/MemoryArchivePage.jsx"; function App() { - return ; - - /* - return ( -
-
-
-

Nacimiento - My Egg

-

- Frontend setup is ready. -

- -
-
-
+ return ( + + + } /> + } /> + } /> + } /> + + ); - */ } export default App; \ No newline at end of file diff --git a/src/pages/MemoryArchivePage.jsx b/src/pages/MemoryArchivePage.jsx index ab42b34..8c58cae 100644 --- a/src/pages/MemoryArchivePage.jsx +++ b/src/pages/MemoryArchivePage.jsx @@ -226,7 +226,7 @@ function MemoryArchivePage() {

- ⌕ View + ⌕ View diff --git a/src/pages/ViewPostPage.jsx b/src/pages/ViewPostPage.jsx index 96c0f55..15219ac 100644 --- a/src/pages/ViewPostPage.jsx +++ b/src/pages/ViewPostPage.jsx @@ -1,6 +1,41 @@ +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { getPostById } from "../api/postsApi"; import "./ViewPostPage.css"; function ViewPostPage() { + const { id } = useParams(); + const [post, setPost] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function loadPost() { + const foundPost = await getPostById(id); + + setPost(foundPost || null); + setLoading(false); + } + + loadPost(); + }, [id]); + + if (loading) { + return ( +
+

Loading memory...

+
+ ); + } + + if (!post) { + return ( +
+

Post not found.

+ ← Back to Archive +
+ ); + } + return (
@@ -27,101 +62,21 @@ function ViewPostPage() {
-

Reflections on the project Nacimiento

+

{post.title}

- ▣ May 26, 2026 - study - ◉ private - ⌁ 342 words - ✧ +15 Will + ▣ {new Date(post.created_at).toLocaleDateString()} + {post.tag} + ◉ {post.visibility} + ⌁ {post.word_count} words + ✧ +{post.will_reward} Will
-

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

- -

- Tomorrow, I plan to focus on the structure of the new project. But - for today, sitting with these scattered thoughts feels sufficient. - The progress egg glows a little brighter, a small validation of - simply being present. -

+ {post.content.split("\n").map((paragraph, index) => ( +

{paragraph}

+ ))}
From c60fa794ba63f10962f8b5221ee78c66ecea3abf Mon Sep 17 00:00:00 2001 From: Driedoutjerky Date: Fri, 29 May 2026 17:33:00 +0900 Subject: [PATCH 5/8] feat: delete button works on both archive page and view page --- src/api/postsApi.js | 17 +++++++++++++++++ src/pages/MemoryArchivePage.jsx | 25 +++++++++++++++++++++++-- src/pages/ViewPostPage.jsx | 30 +++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/api/postsApi.js b/src/api/postsApi.js index 8e0ade4..a8f605a 100644 --- a/src/api/postsApi.js +++ b/src/api/postsApi.js @@ -99,4 +99,21 @@ export async function createPost(postData) { savePostsToStorage(updatedPosts); return newPost; +} + +export async function deletePost(postId) { + const posts = loadPostsFromStorage(); + + console.log("Before delete:", posts); + console.log("Trying to delete postId:", postId); + + const updatedPosts = posts.filter( + (post) => Number(post.post_id) !== Number(postId) + ); + + console.log("After delete:", updatedPosts); + + savePostsToStorage(updatedPosts); + + return true; } \ No newline at end of file diff --git a/src/pages/MemoryArchivePage.jsx b/src/pages/MemoryArchivePage.jsx index 8c58cae..fad0cb0 100644 --- a/src/pages/MemoryArchivePage.jsx +++ b/src/pages/MemoryArchivePage.jsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { getAllPosts } from "../api/postsApi"; +import { deletePost, getAllPosts } from "../api/postsApi"; import "./MemoryArchivePage.css"; @@ -106,6 +106,23 @@ function MemoryArchivePage() { loadArchivePosts(); }, []); + async function handleDeleteSelectedPost() { + const confirmed = window.confirm("Delete this memory?"); + + if (!confirmed) { + return; + } + + await deletePost(selectedPost.post_id); + + const updatedPosts = archivePosts.filter( + (post) => Number(post.post_id) !== Number(selectedPost.post_id) + ); + + setArchivePosts(updatedPosts); + setSelectedPost(updatedPosts[0] || null); + } + if (loading) { return (
@@ -227,7 +244,11 @@ function MemoryArchivePage() {
⌕ View -
diff --git a/src/pages/ViewPostPage.jsx b/src/pages/ViewPostPage.jsx index 15219ac..bdc08d4 100644 --- a/src/pages/ViewPostPage.jsx +++ b/src/pages/ViewPostPage.jsx @@ -1,10 +1,11 @@ import { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; -import { getPostById } from "../api/postsApi"; +import { useNavigate, useParams } from "react-router-dom"; +import { deletePost, getPostById } from "../api/postsApi"; import "./ViewPostPage.css"; function ViewPostPage() { const { id } = useParams(); + const navigate = useNavigate(); const [post, setPost] = useState(null); const [loading, setLoading] = useState(true); @@ -36,6 +37,25 @@ function ViewPostPage() { ); } + async function handleDelete() { + console.log("Delete button clicked"); + console.log("Current post:", post); + + const confirmed = window.confirm("Delete this memory?"); + + console.log("Confirmed:", confirmed); + + if (!confirmed) { + return; + } + + await deletePost(post.post_id); + + console.log("Deleted post id:", post.post_id); + + navigate("/archive"); + } + return (
@@ -81,7 +101,11 @@ function ViewPostPage() {
-
From 31f5fcfa25275015f319510b67f9e48f913649e9 Mon Sep 17 00:00:00 2001 From: Driedoutjerky Date: Fri, 29 May 2026 17:50:32 +0900 Subject: [PATCH 6/8] feat:writing redirection,tag selector design,visibility color fix --- src/pages/WritePostPage.css | 101 ++++++++++++++++++++++++++++-------- src/pages/WritePostPage.jsx | 34 ++++++------ src/styles/global.css | 12 ++++- 3 files changed, 107 insertions(+), 40 deletions(-) diff --git a/src/pages/WritePostPage.css b/src/pages/WritePostPage.css index 179a7d2..499c93e 100644 --- a/src/pages/WritePostPage.css +++ b/src/pages/WritePostPage.css @@ -1,5 +1,8 @@ -.write-post-page { +.app-page.write-post-page { min-height: 100vh; + min-height: 100dvh; + width: 100%; + overflow-x: hidden; --write-bg: var(--color-bg); --write-bg-soft: var(--color-bg-soft); @@ -13,11 +16,12 @@ --write-toggle-bg: #f4dfce; --write-button-text: #fff8ef; - background: var(--write-bg); + background-color: var(--write-bg); color: var(--write-text); + transition: background-color 180ms ease, color 180ms ease; } -.write-post-page.visibility-private { +.app-page.write-post-page.visibility-private { --write-bg: #eee9df; --write-bg-soft: #f4efe6; --write-card-bg: #fffdf8; @@ -30,7 +34,7 @@ --write-toggle-bg: #f4dfce; } -.write-post-page.visibility-public { +.app-page.write-post-page.visibility-public { --write-bg: #eef1e7; --write-bg-soft: #f6f7ef; --write-card-bg: #fffef7; @@ -43,7 +47,7 @@ --write-toggle-bg: #e2ecd7; } -.write-post-page.visibility-anonymous { +.app-page.write-post-page.visibility-anonymous { --write-bg: #e9e8eb; --write-bg-soft: #f2f0f4; --write-card-bg: #fffafe; @@ -72,7 +76,7 @@ .write-brand { font-family: var(--font-serif); font-size: 1.05rem; - color: #4d342c; + color: var(--write-accent-dark); } .write-nav { @@ -88,8 +92,8 @@ justify-content: center; padding: 0 0.8rem; border-radius: 999px; - background: #ded8cc; - color: #5b4b41; + background: var(--write-toggle-bg); + color: var(--write-accent-dark); font-size: 0.75rem; font-weight: 700; } @@ -111,19 +115,19 @@ font-size: 0.55rem; font-weight: 700; letter-spacing: 0.14em; - color: #5f473d; + color: var(--write-text-soft); } .write-user-text strong { font-family: var(--font-serif); font-size: 0.9rem; - color: #3c2a24; + color: var(--write-accent-dark); } .write-avatar { width: 32px; height: 32px; - border: 1.3px solid #7b6658; + border: 1.3px solid var(--write-accent); border-radius: 50%; position: relative; } @@ -135,7 +139,7 @@ left: 50%; width: 8px; height: 8px; - border: 1.3px solid #7b6658; + border: 1.3px solid var(--write-accent); border-radius: 50%; transform: translateX(-50%); } @@ -147,7 +151,7 @@ bottom: 5px; width: 18px; height: 10px; - border: 1.3px solid #7b6658; + border: 1.3px solid var(--write-accent); border-bottom: none; border-radius: 999px 999px 0 0; transform: translateX(-50%); @@ -210,11 +214,11 @@ width: 100%; padding: 0 0 0.9rem; border: none; - border-bottom: 1px solid #e7ded2; + border-bottom: 1px solid var(--write-card-border); background: transparent; font-family: var(--font-serif); font-size: 1.75rem; - color: #2f241f; + color: var(--write-text); } .write-title-input::placeholder { @@ -223,12 +227,14 @@ .write-title-input:focus { outline: none; - border-bottom-color: #bda183; + border-bottom-color: var(--write-accent); } -.write-body-label { +.write-body-label span { display: block; - margin-top: 1.25rem; + margin-bottom: 0.35rem; + color: var(--write-text-soft); + font-size: 0.8rem; } .write-body-label span { @@ -255,7 +261,7 @@ } .write-body-input:focus { - outline: 1px solid #c6aa8a; + outline: 1px solid var(--write-accent); } /* Image upload */ @@ -295,6 +301,55 @@ color: #9b9188; } +/* Tag selector */ + +.write-tag-field { + display: flex; + flex-direction: column; + gap: 0.45rem; + width: min(100%, 260px); + color: var(--write-text-soft); + font-size: 0.68rem; + font-weight: 900; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.write-tag-select-wrap { + position: relative; +} + +.write-tag-select-wrap::after { + content: "⌄"; + position: absolute; + top: 50%; + right: 0.9rem; + transform: translateY(-50%); + color: var(--write-accent); + pointer-events: none; + font-size: 0.8rem; +} + +.write-tag-select { + width: 100%; + min-height: 38px; + appearance: none; + border: 1px solid var(--write-card-border); + border-radius: 999px; + background: var(--write-card-bg); + color: var(--write-text); + padding: 0 2.3rem 0 1rem; + font-size: 0.78rem; + font-weight: 800; + cursor: pointer; + box-shadow: 0 10px 20px rgba(73, 54, 41, 0.04); +} + +.write-tag-select:focus { + outline: 2px solid color-mix(in srgb, var(--write-accent) 40%, transparent); + border-color: var(--write-accent); +} + /* Bottom bar */ .write-bottom-bar { @@ -376,7 +431,7 @@ .memory-stats-card h2 { margin: 0 0 1rem; - color: #62544c; + color: var(--write-accent-dark); font-size: 0.75rem; font-weight: 800; letter-spacing: 0.12em; @@ -395,18 +450,18 @@ } .memory-stat-row span { - color: #8a7d73; + color: var(--write-text-soft); font-size: 0.68rem; } .memory-stat-row strong { - color: #3a2a23; + color: var(--write-text); font-size: 0.85rem; } .memory-note { margin: 1.35rem 0 0 0.55rem; - color: #7f756c; + color: var(--write-text-soft); font-size: 0.72rem; font-style: italic; line-height: 1.45; diff --git a/src/pages/WritePostPage.jsx b/src/pages/WritePostPage.jsx index 084bc54..43a71a4 100644 --- a/src/pages/WritePostPage.jsx +++ b/src/pages/WritePostPage.jsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useNavigate } from "react-router-dom"; import { createPost } from "../api/postsApi"; import "./WritePostPage.css"; @@ -10,6 +11,8 @@ function WritePostPage() { const [imageUrl, setImageUrl] = useState(""); const [visibility, setVisibility] = useState("private"); + const navigate = useNavigate(); + {/*NOTE: LOGICS SHOULD BE IMPLEMENTED IN BACKEND. THESE ARE TEMPORARY PLACEHOLDERS */} const wordCount = content.trim() ? content.trim().split(/\s+/).length : 0; const estimatedWill = Math.max(1, Math.floor(wordCount / 10)); @@ -31,14 +34,8 @@ function WritePostPage() { }); console.log("Created post:", newPost); - alert("Post created!"); - - setTitle(""); - setContent(""); - setTag("reflection"); - setImageUrl(""); - setVisibility("private"); + navigate("/archive"); } return ( @@ -99,14 +96,21 @@ function WritePostPage() { -