From 59344d03e9c2924c1ffbd05dc6b46a224162ed39 Mon Sep 17 00:00:00 2001 From: PJW Date: Sat, 4 Jul 2026 14:49:49 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat=20:=20WEB-9=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9E=90=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/images/deleteicon.svg | 3 + src/assets/images/editicon.svg | 3 + src/pages/admin/AdminPage.jsx | 17 +- src/pages/visitor/main/ActivitiesSection.jsx | 125 ++++- src/pages/visitor/main/RecruitSection.jsx | 164 +++++-- src/pages/visitor/main/TimelineSection.jsx | 474 +++++++++++++++---- src/stores/homeContentStore.js | 44 ++ 7 files changed, 678 insertions(+), 152 deletions(-) create mode 100644 src/assets/images/deleteicon.svg create mode 100644 src/assets/images/editicon.svg create mode 100644 src/stores/homeContentStore.js diff --git a/src/assets/images/deleteicon.svg b/src/assets/images/deleteicon.svg new file mode 100644 index 0000000..a3058df --- /dev/null +++ b/src/assets/images/deleteicon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/editicon.svg b/src/assets/images/editicon.svg new file mode 100644 index 0000000..9bbb489 --- /dev/null +++ b/src/assets/images/editicon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/pages/admin/AdminPage.jsx b/src/pages/admin/AdminPage.jsx index 6a8b0c0..e3a1ca1 100644 --- a/src/pages/admin/AdminPage.jsx +++ b/src/pages/admin/AdminPage.jsx @@ -1,8 +1,21 @@ +import ActivitiesSection from '@/pages/visitor/main/ActivitiesSection'; +import TimelineSection from '@/pages/visitor/main/TimelineSection'; +import RecruitSection from '@/pages/visitor/main/RecruitSection'; +import HeroSection from '../visitor/main/HeroSection'; + +/** + * 관리자 홈 콘텐츠 관리 페이지. + * 주요 활동/ONE 활동 현황/신입 부원 모집 섹션을 그대로 재사용한다. + * isEditable을 켜서 렌더링하므로, 관리자 로그인 상태(isAuthenticated)일 때만 수정/삭제 아이콘을 노출한다. + * 방문자용 HomePage는 isEditable을 넘기지 않아 로그인 여부와 무관하게 항상 일반 화면만 보인다. + */ function AdminPage() { return (
-

관리자 페이지

-

신청 부원 정보, 수상경력, 연간 계획, 동아리 소개, 명부 관리 (CRUD)

+ + + +
); } diff --git a/src/pages/visitor/main/ActivitiesSection.jsx b/src/pages/visitor/main/ActivitiesSection.jsx index 060b6d0..3cfec47 100644 --- a/src/pages/visitor/main/ActivitiesSection.jsx +++ b/src/pages/visitor/main/ActivitiesSection.jsx @@ -1,8 +1,39 @@ +import { useState } from 'react'; import useScrollReveal from '@/hooks/useScrollReveal'; -import { ACTIVITY_LIST } from '@/constants/homeData'; +import useAuthStore from '@/stores/authStore'; +import useHomeContentStore from '@/stores/homeContentStore'; +import editIcon from '@/assets/images/editicon.svg'; +import deleteIcon from '@/assets/images/deleteicon.svg'; -export default function ActivitiesSection() { +export default function ActivitiesSection({ isEditable = false }) { const revealRef = useScrollReveal(); + const isAuthenticated = useAuthStore((state) => state.isAuthenticated) && isEditable; + const activities = useHomeContentStore((state) => state.activities); + const updateActivity = useHomeContentStore((state) => state.updateActivity); + + const [editingIndex, setEditingIndex] = useState(null); + const [draftTitle, setDraftTitle] = useState(''); + const [draftDescription, setDraftDescription] = useState(''); + + const startEdit = (index) => { + setEditingIndex(index); + setDraftTitle(activities[index].title); + setDraftDescription(activities[index].description); + }; + + const handleSaveClick = (index) => { + if (!draftTitle.trim() || !draftDescription.trim()) { + alert('내용을 입력해 주세요.'); + return; + } + updateActivity(index, { title: draftTitle, description: draftDescription }); + setEditingIndex(null); + }; + + const handleDeleteContent = (index) => { + updateActivity(index, { title: '', description: '' }); + setEditingIndex((previous) => (previous === index ? null : previous)); + }; return (
@@ -15,27 +46,77 @@ export default function ActivitiesSection() { {/* 2×2 카드 그리드 */}
- {ACTIVITY_LIST.map((activity, index) => ( -
- {/* 원형 아이콘 컨테이너 */} -
- {activity.title} + {activities.map((activity, index) => { + const isEditing = editingIndex === index; + return ( +
+ {isAuthenticated && ( +
+ + +
+ )} + + {/* 원형 아이콘 컨테이너 */} +
+ {activity.title} +
+ + {isEditing ? ( + <> + setDraftTitle(event.target.value)} + placeholder="활동 제목" + className="mb-5 w-full rounded-md border border-line px-3 py-2 text-center text-lg font-bold text-ink" + /> +