diff --git a/client/public/postcard_back.png b/client/public/postcard_back.png deleted file mode 100644 index 783bbeb1..00000000 Binary files a/client/public/postcard_back.png and /dev/null differ diff --git a/client/public/postcard_front.png b/client/public/postcard_front.png deleted file mode 100644 index da4c9219..00000000 Binary files a/client/public/postcard_front.png and /dev/null differ diff --git a/client/public/retreat/header/Calling.png b/client/public/retreat/header/Calling.png new file mode 100644 index 00000000..7c43d1d6 Binary files /dev/null and b/client/public/retreat/header/Calling.png differ diff --git a/client/public/retreat/header/regist_button.png b/client/public/retreat/header/regist_button.png new file mode 100644 index 00000000..06dc02d8 Binary files /dev/null and b/client/public/retreat/header/regist_button.png differ diff --git a/client/public/retreat/intro/bg_img.png b/client/public/retreat/intro/bg_img.png new file mode 100644 index 00000000..b56f682a Binary files /dev/null and b/client/public/retreat/intro/bg_img.png differ diff --git a/client/public/retreat/login/Calling.png b/client/public/retreat/login/Calling.png new file mode 100644 index 00000000..f0b9b1e4 Binary files /dev/null and b/client/public/retreat/login/Calling.png differ diff --git a/client/public/retreat/login/bottom_bg.png b/client/public/retreat/login/bottom_bg.png deleted file mode 100644 index 4d9d11b1..00000000 Binary files a/client/public/retreat/login/bottom_bg.png and /dev/null differ diff --git a/client/public/retreat/login/btn.png b/client/public/retreat/login/btn.png deleted file mode 100644 index bb0ae4f1..00000000 Binary files a/client/public/retreat/login/btn.png and /dev/null differ diff --git a/client/public/retreat/login/kakao.png b/client/public/retreat/login/kakao.png new file mode 100644 index 00000000..70ff1842 Binary files /dev/null and b/client/public/retreat/login/kakao.png differ diff --git a/client/public/retreat/login/top_bg.png b/client/public/retreat/login/top_bg.png deleted file mode 100644 index ba847357..00000000 Binary files a/client/public/retreat/login/top_bg.png and /dev/null differ diff --git a/client/public/retreat/main/bottom.png b/client/public/retreat/main/bottom.png deleted file mode 100644 index e1c6b545..00000000 Binary files a/client/public/retreat/main/bottom.png and /dev/null differ diff --git a/client/public/retreat/main/fifth/bottom.png b/client/public/retreat/main/fifth/bottom.png new file mode 100644 index 00000000..03a6655a Binary files /dev/null and b/client/public/retreat/main/fifth/bottom.png differ diff --git a/client/public/retreat/main/fifth_top.png b/client/public/retreat/main/fifth_top.png deleted file mode 100644 index d012dd6d..00000000 Binary files a/client/public/retreat/main/fifth_top.png and /dev/null differ diff --git a/client/public/retreat/main/first/2026_SUMMER_RETREAT.png b/client/public/retreat/main/first/2026_SUMMER_RETREAT.png new file mode 100644 index 00000000..04e4c190 Binary files /dev/null and b/client/public/retreat/main/first/2026_SUMMER_RETREAT.png differ diff --git a/client/public/retreat/main/first/Calling.png b/client/public/retreat/main/first/Calling.png new file mode 100644 index 00000000..84007f71 Binary files /dev/null and b/client/public/retreat/main/first/Calling.png differ diff --git a/client/public/retreat/main/first/SUWONJEILYOUNGPEOPLE.png b/client/public/retreat/main/first/SUWONJEILYOUNGPEOPLE.png new file mode 100644 index 00000000..e667c3b6 Binary files /dev/null and b/client/public/retreat/main/first/SUWONJEILYOUNGPEOPLE.png differ diff --git a/client/public/retreat/main/first/bible_reference.png b/client/public/retreat/main/first/bible_reference.png new file mode 100644 index 00000000..e1b5f488 Binary files /dev/null and b/client/public/retreat/main/first/bible_reference.png differ diff --git a/client/public/retreat/main/first/date.png b/client/public/retreat/main/first/date.png new file mode 100644 index 00000000..41fe255a Binary files /dev/null and b/client/public/retreat/main/first/date.png differ diff --git a/client/public/retreat/main/first/main_bg.png b/client/public/retreat/main/first/main_bg.png new file mode 100644 index 00000000..b338a9e9 Binary files /dev/null and b/client/public/retreat/main/first/main_bg.png differ diff --git a/client/public/retreat/main/first/main_bottom.png b/client/public/retreat/main/first/main_bottom.png new file mode 100644 index 00000000..21c72bb5 Binary files /dev/null and b/client/public/retreat/main/first/main_bottom.png differ diff --git a/client/public/retreat/main/first/verse.png b/client/public/retreat/main/first/verse.png new file mode 100644 index 00000000..8feebb0c Binary files /dev/null and b/client/public/retreat/main/first/verse.png differ diff --git a/client/public/retreat/main/first_top.png b/client/public/retreat/main/first_top.png deleted file mode 100644 index 36dea262..00000000 Binary files a/client/public/retreat/main/first_top.png and /dev/null differ diff --git a/client/public/retreat/main/fourth/calendar.png b/client/public/retreat/main/fourth/calendar.png new file mode 100644 index 00000000..70c7cc6c Binary files /dev/null and b/client/public/retreat/main/fourth/calendar.png differ diff --git a/client/public/retreat/main/fourth/info.png b/client/public/retreat/main/fourth/info.png new file mode 100644 index 00000000..be08f397 Binary files /dev/null and b/client/public/retreat/main/fourth/info.png differ diff --git a/client/public/retreat/main/fourth/place.png b/client/public/retreat/main/fourth/place.png new file mode 100644 index 00000000..c498407d Binary files /dev/null and b/client/public/retreat/main/fourth/place.png differ diff --git a/client/public/retreat/main/fourth_top.png b/client/public/retreat/main/fourth_top.png deleted file mode 100644 index 147599c7..00000000 Binary files a/client/public/retreat/main/fourth_top.png and /dev/null differ diff --git a/client/public/retreat/main/second/content.png b/client/public/retreat/main/second/content.png new file mode 100644 index 00000000..c5d4d43f Binary files /dev/null and b/client/public/retreat/main/second/content.png differ diff --git a/client/public/retreat/main/second/invitation.png b/client/public/retreat/main/second/invitation.png new file mode 100644 index 00000000..a0924af1 Binary files /dev/null and b/client/public/retreat/main/second/invitation.png differ diff --git a/client/public/retreat/main/second_top.png b/client/public/retreat/main/second_top.png deleted file mode 100644 index 0de44bf6..00000000 Binary files a/client/public/retreat/main/second_top.png and /dev/null differ diff --git a/client/public/retreat/main/third/table1.png b/client/public/retreat/main/third/table1.png new file mode 100644 index 00000000..69984ea8 Binary files /dev/null and b/client/public/retreat/main/third/table1.png differ diff --git a/client/public/retreat/main/third/table2.png b/client/public/retreat/main/third/table2.png new file mode 100644 index 00000000..be5b3daa Binary files /dev/null and b/client/public/retreat/main/third/table2.png differ diff --git a/client/public/retreat/main/third/table3.png b/client/public/retreat/main/third/table3.png new file mode 100644 index 00000000..78ca0362 Binary files /dev/null and b/client/public/retreat/main/third/table3.png differ diff --git a/client/public/retreat/main/third/table4.png b/client/public/retreat/main/third/table4.png new file mode 100644 index 00000000..ccf700fa Binary files /dev/null and b/client/public/retreat/main/third/table4.png differ diff --git a/client/public/retreat/main/third/timetable.png b/client/public/retreat/main/third/timetable.png new file mode 100644 index 00000000..d2f1e3e1 Binary files /dev/null and b/client/public/retreat/main/third/timetable.png differ diff --git a/client/public/retreat/main/third_top.png b/client/public/retreat/main/third_top.png deleted file mode 100644 index c2e310e3..00000000 Binary files a/client/public/retreat/main/third_top.png and /dev/null differ diff --git a/client/public/retreat/retreat_bg.jpeg b/client/public/retreat/retreat_bg.jpeg deleted file mode 100644 index 600dab34..00000000 Binary files a/client/public/retreat/retreat_bg.jpeg and /dev/null differ diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx index 068e2ecf..64889f05 100644 --- a/client/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -25,7 +25,7 @@ export default function RootLayout({ } var title = "새벽이슬" if (global.location?.pathname.includes("retreat")) { - title = "2026 겨울 수련회" + title = "2026 여름 수련회" } return ( diff --git a/client/src/app/leader/postcard/page.tsx b/client/src/app/leader/postcard/page.tsx deleted file mode 100644 index 0cd0176a..00000000 --- a/client/src/app/leader/postcard/page.tsx +++ /dev/null @@ -1,154 +0,0 @@ -"use client" - -import { Button, MenuItem, Select, Stack, TextField } from "@mui/material" -import { Community } from "@server/entity/community" -import { User } from "@server/entity/user" -import { get } from "@/config/api" -import { useEffect, useState } from "react" -import { useRouter } from "next/navigation" -import axios from "@/config/axios" -import { useNotification } from "@/hooks/useNotification" - -export default function PostcardPage() { - const { push } = useRouter() - const [groupName, setGroupName] = useState("") - const [soonList, setSoonList] = useState([]) - const [selectedUser, setSelectedUser] = useState(undefined) - const [textFieldValue, setTextFieldValue] = useState("") - const [localStorageData, setLocalStorageData] = useState<{ - userId: string - text: string - } | null>(null) - const { success } = useNotification() - - useEffect(() => { - fetchGroupDate() - getLocalStorageData() - }, []) - - function getLocalStorageData() { - const data = localStorage.getItem("postcardData") - if (data) { - const parsedData = JSON.parse(data) - if (parsedData.userId && parsedData.text) { - setLocalStorageData(parsedData) - } - } - } - - useEffect(() => { - if (!localStorageData) { - return - } - if (!selectedUser) { - return - } - if (localStorageData.userId == selectedUser.id) { - setTextFieldValue(localStorageData.text) - return - } - setTextFieldValue("") // 다른 사용자를 선택하면 텍스트 필드 초기화 - }, [localStorageData, selectedUser]) - - async function fetchGroupDate() { - const group: Community = await get("/soon/only-my-group-info") - setGroupName(group.name) - setSoonList(group.users) - if (group.users.length > 0) { - setSelectedUser(group.users[0]) // 기본으로 첫 번째 사용자 선택 - } - } - - function saveToLocalStorage() { - if (!selectedUser) return - const postcardData = { - userId: selectedUser.id, - text: textFieldValue, - } - localStorage.setItem("postcardData", JSON.stringify(postcardData)) - } - - function previewPostcard() { - saveToLocalStorage() - push("/retreat/postcard/preview") - } - - async function saveToServer() { - await axios.post("/retreat/set-postcard-content", { - content: textFieldValue, - targetUserId: selectedUser?.id, - }) - localStorage.removeItem("postcardData") - success("저장되었습니다.") - } - - async function getCardContentFromServer() { - if (!selectedUser) return - try { - const response = await axios.get( - `/retreat/get-postcard-content/${selectedUser.id}`, - ) - setTextFieldValue(response.data.content || "") - } catch (error) { - setTextFieldValue("") // 서버에서 가져오지 못하면 텍스트 필드 초기화 - } - } - - useEffect(() => { - getCardContentFromServer() - }, [selectedUser]) - - return ( - - - - 수련회를 어떠한 마음으로 참여하면 좋을지 격려의 편지를 적어주세요!. - 접수 완료후 편지가 나타납니다! - - {groupName} (다락방 / 마을) - - setTextFieldValue(e.target.value)} - onClick={(e) => { - const target = e.target as HTMLInputElement - target.value = textFieldValue - }} - /> - - - - - ) -} diff --git a/client/src/app/leader/postcard/preview/numberToString.ts b/client/src/app/leader/postcard/preview/numberToString.ts deleted file mode 100644 index 03d221b3..00000000 --- a/client/src/app/leader/postcard/preview/numberToString.ts +++ /dev/null @@ -1,74 +0,0 @@ -const stringLessThanTen = [ - "첫", - "두", - "세", - "네", - "다섯", - "여섯", - "일곱", - "여덟", - "아홉", -] - -const numberAtTheOne = [ - "한", - "두", - "세", - "네", - "다섯", - "여섯", - "일곱", - "여덟", - "아홉", -] - -const numberAtTheTen = [ - "열", - "스물", - "서른", - "마흔", - "쉰", - "예순", - "일흔", - "여든", - "아흔", -] - -const numberAtTheHundred = [ - "백", - "이백", - "삼백", - "사백", - "오백", - "육백", - "칠백", - "팔백", - "구백", -] - -export default function numberToString(num: number | undefined): string { - if (!num) { - return "" - } - if (num < 10) { - return "" + stringLessThanTen[num - 1] - } - let returnString = "" - if (num >= 100) { - returnString += numberAtTheHundred[Math.floor(num / 100) - 1] - num = num % 100 - } - if (num >= 10) { - if (num === 20) { - returnString += "스무" - } else { - returnString += numberAtTheTen[Math.floor(num / 10) - 1] - } - num = num % 10 - } - if (num > 0) { - returnString += numberAtTheOne[num - 1] - } - //20 예외처리 - return returnString -} diff --git a/client/src/app/leader/postcard/preview/page.module.css b/client/src/app/leader/postcard/preview/page.module.css deleted file mode 100644 index fdbf0a98..00000000 --- a/client/src/app/leader/postcard/preview/page.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.postcard { - width: fit-content; - height: fit-content; - transform-style: preserve-3d; - border: 1px solid #ddd; - - transition: all 0.5s linear; -} - -.postcard-image { - width: calc(100vw - 70px); - object-fit: contain; - height: auto; -} - -.postcard-back { - background-image: url(/postcard_back.png); - background-color: #f2e8de; - background-size: contain; -} - -.moving-animation { - animation: moving 4s infinite; - animation-direction: initial; -} - -@keyframes moving { - 0% { - transform: rotateY(0deg) rotateX(0deg); - } - 33% { - transform: rotateY(10deg) rotateX(20deg); - } - 66% { - transform: rotateY(-10deg) rotateX(-20deg); - } - 100% { - transform: rotateY(0deg) rotateX(0deg); - } -} diff --git a/client/src/app/leader/postcard/preview/page.tsx b/client/src/app/leader/postcard/preview/page.tsx deleted file mode 100644 index 3028d01f..00000000 --- a/client/src/app/leader/postcard/preview/page.tsx +++ /dev/null @@ -1,200 +0,0 @@ -"use client" - -import Image from "next/image" -import { Box, Stack } from "@mui/material" -import styles from "./page.module.css" -import { useEffect, useRef, useState } from "react" - -let startPositionX = 0 -let startPositionY = 0 - -let currentRotateX = 0 -let currentRotateY = 0 - -export default function Postcard() { - const [rotateX, setRotateX] = useState(0) - const [rotateY, setRotateY] = useState(0) - const [isMoving, setIsMoving] = useState(false) - const [backWidth, setBackWidth] = useState(0) - const [backHeight, setBackHeight] = useState(0) - const [isNotMove, setIsNotMove] = useState(true) - const [postContent, setPostContent] = useState("") - const [fontSize, setFontSize] = useState(0) - - const imageRef = useRef(null) - const contentRef = useRef(null) - - useEffect(() => { - function touchStart(e: TouchEvent) { - startPositionX = e.touches[0].clientX + currentRotateY * 2 - startPositionY = e.touches[0].clientY + currentRotateX * 2 - - setIsMoving(true) - setIsNotMove(false) - } - addEventListener("touchstart", touchStart) - - function touchMove(e: TouchEvent) { - const diffX = e.touches[0].clientX - startPositionX - const diffY = e.touches[0].clientY - startPositionY - - currentRotateX = diffY * -0.5 - currentRotateY = diffX * 0.5 - - const isReverse = Math.floor((currentRotateX + 90) / 180) % 2 !== 0 - //뒤집혀 있으면 죄우로 긁었을때, 반대로 움직여야 함 - if (isReverse) { - currentRotateY = currentRotateY * -1 - } - - setRotateX(currentRotateX) - setRotateY(currentRotateY) - } - addEventListener("touchmove", touchMove) - - function touchEnd() { - setIsMoving(false) - const tempRotateX = Math.round(currentRotateX / 180.0) - currentRotateX = tempRotateX * 180 - setRotateX(currentRotateX) - - const tempRotateY = Math.round(currentRotateY / 180.0) - currentRotateY = tempRotateY * 180 - setRotateY(currentRotateY) - } - - addEventListener("touchend", touchEnd) - return () => { - removeEventListener("touchstart", touchStart) - removeEventListener("touchmove", touchMove) - removeEventListener("touchend", touchEnd) - } - }, []) - - useEffect(() => { - ;(async () => { - if (imageRef.current) { - // 이미지가 로드되면 뒷면의 크기를 설정 - while (imageRef.current.clientHeight === 0) { - await new Promise((resolve) => setTimeout(resolve, 100)) - } - setFontSize(backHeight / 40) - setBackWidth(imageRef.current.clientWidth) - setBackHeight(imageRef.current.clientHeight) - } - })() - }, [imageRef.current]) - - useEffect(() => { - ;(async () => { - if (!contentRef.current) { - return - } - // 컨텐츠가 로드되면 뒷면의 크기를 설정 - while ( - contentRef.current.clientHeight === 0 || - backWidth === 0 || - backHeight === 0 - ) { - await new Promise((resolve) => setTimeout(resolve, 100)) - } - - while (contentRef.current.clientHeight <= backWidth * 0.9) { - await new Promise((resolve) => setTimeout(resolve, 1)) - setFontSize((pre) => pre * 1.015) - } - })() - }, [contentRef.current]) - - useEffect(() => { - if (typeof window !== "undefined") { - const data = localStorage.getItem("postcardData") - - const localStorageData = localStorage.getItem("postcardData") - if (!localStorageData) { - return - } - const parsedData = JSON.parse(localStorageData) - if (!parsedData || !parsedData.text) { - return - } - setPostContent(parsedData.text) - } - }, []) - - return ( - - - - - - - - - - {postContent} - - - - ) -} diff --git a/client/src/app/retreat/complete/page.tsx b/client/src/app/retreat/complete/page.tsx new file mode 100644 index 00000000..88dce2b9 --- /dev/null +++ b/client/src/app/retreat/complete/page.tsx @@ -0,0 +1,77 @@ +"use client" + +import { Box, Button, IconButton, Stack } from "@mui/material" +import ArrowBackIosNewRoundedIcon from "@mui/icons-material/ArrowBackIosNewRounded" +import { useRouter, useSearchParams } from "next/navigation" +import { Suspense } from "react" +import { useNotification } from "@/hooks/useNotification" + +export default function RetreatRegister() { + const { push } = useRouter() + + function handleGoToBack() { + push("/retreat") + } + + return ( + + + + + + 수련회 신청 + + + + 수련회 신청이 완료되었습니다! + + + Loading...}> + + + + ) +} + +function Content() { + const { success } = useNotification() + const searchParams = useSearchParams() + const isWorker = searchParams.get("isWorker") === "true" + + function copyToClipboard() { + const accountInfo = `3333342703455 카카오뱅크\n + ${isWorker ? "150,000원" : "120,000원"}` + navigator.clipboard.writeText(accountInfo) + success("계좌 정보가 복사되었습니다.") + } + + return ( + + + 입금 계좌 + 3333342703455 카카오뱅크 (성은비) + 수련회비 + + + {isWorker ? "직장인" : "학생"}:{" "} + {isWorker ? "150,000원" : "120,000원"} + + + + + ) +} diff --git a/client/src/app/retreat/components/Header.tsx b/client/src/app/retreat/components/Header.tsx new file mode 100644 index 00000000..52291118 --- /dev/null +++ b/client/src/app/retreat/components/Header.tsx @@ -0,0 +1,44 @@ +"use client" + +import { Stack } from "@mui/material" +import { useRouter } from "next/navigation" + +export default function RetreatHeader() { + const { push } = useRouter() + + return ( + + + Calling + regist_button push("/retreat/login")} + /> + + + ) +} diff --git a/client/src/app/retreat/hooks/useRetreat.ts b/client/src/app/retreat/hooks/useRetreat.ts index a465f1dc..f5845265 100644 --- a/client/src/app/retreat/hooks/useRetreat.ts +++ b/client/src/app/retreat/hooks/useRetreat.ts @@ -2,12 +2,10 @@ import axios from "@/config/axios" import useAuth from "@/hooks/useAuth" import { atom, useAtom } from "jotai" -const stepAtom = atom(2) const isHalfAtom = atom(null) const isWorkerAtom = atom(null) export default function useRetreat() { - const [step, setStep] = useAtom(stepAtom) const [isHalf, setIsHalf] = useAtom(isHalfAtom) const [isWorker, setIsWorker] = useAtom(isWorkerAtom) @@ -47,8 +45,6 @@ export default function useRetreat() { } return { - step, - setStep, isHalf, setIsHalf, isWorker, diff --git a/client/src/app/retreat/intro/page.module.css b/client/src/app/retreat/intro/page.module.css new file mode 100644 index 00000000..9e8be8e0 --- /dev/null +++ b/client/src/app/retreat/intro/page.module.css @@ -0,0 +1,55 @@ +.page { + width: 100%; +} + +.image_wrapper { + position: relative; + width: 100%; +} + +.main_bg { + width: 100%; + height: auto; + display: block; + z-index: 10; +} + +.text_overlay { + position: absolute; + inset: 0; + z-index: 20; +} + +.text_item { + position: absolute; + left: 50%; + width: min(82vw, 360px); + transform: translate(-50%, 28px); + margin: 0; + text-align: center; + white-space: pre-line; + line-height: 1.55; + font-size: 1.12rem; + font-weight: 600; + letter-spacing: 0.02em; + opacity: 0; + transition: + opacity 700ms ease, + transform 700ms ease; + will-change: opacity, transform; + text-shadow: 0 1px 2px rgba(255, 255, 255, 0.2); +} + +.text_item_visible { + opacity: 1; + transform: translate(-50%, 0); +} + +.start_button { + width: 90%; + z-index: 30; + position: absolute; + left: 50%; + bottom: 18px; + transform: translateX(-50%); +} diff --git a/client/src/app/retreat/intro/page.tsx b/client/src/app/retreat/intro/page.tsx new file mode 100644 index 00000000..a2ae187c --- /dev/null +++ b/client/src/app/retreat/intro/page.tsx @@ -0,0 +1,127 @@ +"use client" + +import { useEffect, useRef, useState } from "react" +import styles from "./page.module.css" +import { Button, Stack } from "@mui/material" +import { useRouter } from "next/navigation" + +const texts: Array<{ + text: string + color: string + top: string +}> = [ + { + text: "사울이 길을 가다가\n다메섹에 가까이 이르더니", + color: "FCF0E3", + top: "5%", + }, + { + text: "홀연히 하늘로부터\n빛이 그를 둘러 비추는지라", + color: "FCF0E3", + top: "16%", + }, + { + text: "사울아 사울아\n네가 어찌하여 나를 박해하느냐", + color: "FCF0E3", + top: "25%", + }, + { + text: "주여 누구시니이까 ", + color: "FCF0E3", + top: "32%", + }, + { + text: "나는 네가 박해하는 예수라", + color: "E87C7C", + top: "46.5%", + }, + { + text: "너는 일어나 시내로 들어가라 \n네가 행할 것을 \n네게 이를 자가 있느니라", + color: "363232", + top: "64%", + }, +] + +export default function RetreatIntroPage() { + const textRefs = useRef>([]) + const [visibleIndexes, setVisibleIndexes] = useState([]) + const { push } = useRouter() + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + setVisibleIndexes((currentVisible) => { + const nextVisible = new Set(currentVisible) + + entries.forEach((entry) => { + const index = Number(entry.target.getAttribute("data-index")) + + if (entry.isIntersecting) { + nextVisible.add(index) + } + }) + + return Array.from(nextVisible).sort((left, right) => left - right) + }) + }, + { + threshold: 0.35, + rootMargin: "0px 0px -12% 0px", + }, + ) + + textRefs.current.forEach((element) => { + if (element) { + observer.observe(element) + } + }) + + return () => { + observer.disconnect() + } + }, []) + + return ( + + + + + {texts.map((item, index) => ( +

{ + textRefs.current[index] = element + }} + data-index={index} + className={`${styles.text_item} ${ + visibleIndexes.includes(index) ? styles.text_item_visible : "" + }`} + style={{ + color: `#${item.color}`, + top: item.top, + }} + > + {item.text} +

+ ))} +
+ +
+
+ ) +} diff --git a/client/src/app/retreat/login/page.tsx b/client/src/app/retreat/login/page.tsx index 18d83374..da18319a 100644 --- a/client/src/app/retreat/login/page.tsx +++ b/client/src/app/retreat/login/page.tsx @@ -1,7 +1,7 @@ "use client" -import { Stack } from "@mui/material" -import RetreatButton from "../components/Button" +import { Box, IconButton, Stack } from "@mui/material" +import ArrowBackIosNewRoundedIcon from "@mui/icons-material/ArrowBackIosNewRounded" import { useRouter } from "next/navigation" import usePageColor from "@/hooks/usePageColor" import useBodyOverflowHidden from "@/hooks/useBodyOverflowHidden" @@ -15,34 +15,64 @@ export default function RetreatLogin() { async function handleKakaoLogin() { try { - await executeKakaoLogin("/retreat") + await executeKakaoLogin("/retreat/register") } catch { - push("/retreat?newUser=true") + push("/retreat/register?newUser=true") } } + + function handleGoToBack() { + push("/retreat") + } + return ( + + + - top background - - + + + 반가워요! + + + 로그인 후 신청을 시작할 수 있어요. + - + login bottom + bottom background diff --git a/client/src/app/retreat/page.tsx b/client/src/app/retreat/page.tsx index c32e9fac..a3e4c9ac 100644 --- a/client/src/app/retreat/page.tsx +++ b/client/src/app/retreat/page.tsx @@ -1,17 +1,13 @@ "use client" -import useAuth from "@/hooks/useAuth" -import { Stack } from "@mui/material" -import { useRouter, useSearchParams } from "next/navigation" -import { Suspense, useEffect, useState } from "react" -import FirstStep from "./steps/first" -import SecondStep from "./steps/second" -import ThirdStep from "./steps/third" -import useRetreat from "./hooks/useRetreat" -import FourthStep from "./steps/fourth" -import FifthStep from "./steps/fifthStep" +import { Box, Stack } from "@mui/material" +import { Suspense } from "react" import usePageColor from "@/hooks/usePageColor" -import useBodyOverflowHidden from "@/hooks/useBodyOverflowHidden" +import Header from "./components/Header" +import RetreatMainFirst from "./sections/first" +import RetreatMainSecond from "./sections/second" +import RetreatThird from "./sections/third" +import RetreatMainFourth from "./sections/fourth" export default function RetreatPage() { return ( @@ -22,72 +18,28 @@ export default function RetreatPage() { } function RetreatContent() { - useBodyOverflowHidden() usePageColor("#2F3237") - const searchParams = useSearchParams() - const isNewUser = searchParams.get("newUser") - const { isLogin } = useAuth() - const { push } = useRouter() - - const { step, setStep } = useRetreat() - - useEffect(() => { - if (isNewUser === "true") { - setStep(1) - } - }, [isNewUser]) - - useEffect(() => { - if (!isLogin && isNewUser !== "true") { - push("/retreat/login") - } - }, [isLogin]) - - var topImageUrl = "" - if (step === 1) { - topImageUrl = "/retreat/main/first_top.png" - } else if (step === 2) { - topImageUrl = "/retreat/main/second_top.png" - } else if (step === 3) { - topImageUrl = "/retreat/main/third_top.png" - } else if (step === 4) { - topImageUrl = "/retreat/main/fourth_top.png" - } else if (step === 5) { - topImageUrl = "/retreat/main/fifth_top.png" - } return ( - - - {`step - - {step === 1 && } - {step === 2 && } - {step === 3 && } - {step === 4 && } - {step === 5 && } - - - - first top - - +
+ + + + + + + Bottom ) } diff --git a/client/src/app/retreat/postcard/numberToString.ts b/client/src/app/retreat/postcard/numberToString.ts deleted file mode 100644 index 03d221b3..00000000 --- a/client/src/app/retreat/postcard/numberToString.ts +++ /dev/null @@ -1,74 +0,0 @@ -const stringLessThanTen = [ - "첫", - "두", - "세", - "네", - "다섯", - "여섯", - "일곱", - "여덟", - "아홉", -] - -const numberAtTheOne = [ - "한", - "두", - "세", - "네", - "다섯", - "여섯", - "일곱", - "여덟", - "아홉", -] - -const numberAtTheTen = [ - "열", - "스물", - "서른", - "마흔", - "쉰", - "예순", - "일흔", - "여든", - "아흔", -] - -const numberAtTheHundred = [ - "백", - "이백", - "삼백", - "사백", - "오백", - "육백", - "칠백", - "팔백", - "구백", -] - -export default function numberToString(num: number | undefined): string { - if (!num) { - return "" - } - if (num < 10) { - return "" + stringLessThanTen[num - 1] - } - let returnString = "" - if (num >= 100) { - returnString += numberAtTheHundred[Math.floor(num / 100) - 1] - num = num % 100 - } - if (num >= 10) { - if (num === 20) { - returnString += "스무" - } else { - returnString += numberAtTheTen[Math.floor(num / 10) - 1] - } - num = num % 10 - } - if (num > 0) { - returnString += numberAtTheOne[num - 1] - } - //20 예외처리 - return returnString -} diff --git a/client/src/app/retreat/postcard/page.module.css b/client/src/app/retreat/postcard/page.module.css deleted file mode 100644 index fdbf0a98..00000000 --- a/client/src/app/retreat/postcard/page.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.postcard { - width: fit-content; - height: fit-content; - transform-style: preserve-3d; - border: 1px solid #ddd; - - transition: all 0.5s linear; -} - -.postcard-image { - width: calc(100vw - 70px); - object-fit: contain; - height: auto; -} - -.postcard-back { - background-image: url(/postcard_back.png); - background-color: #f2e8de; - background-size: contain; -} - -.moving-animation { - animation: moving 4s infinite; - animation-direction: initial; -} - -@keyframes moving { - 0% { - transform: rotateY(0deg) rotateX(0deg); - } - 33% { - transform: rotateY(10deg) rotateX(20deg); - } - 66% { - transform: rotateY(-10deg) rotateX(-20deg); - } - 100% { - transform: rotateY(0deg) rotateX(0deg); - } -} diff --git a/client/src/app/retreat/postcard/page.tsx b/client/src/app/retreat/postcard/page.tsx deleted file mode 100644 index 83cf8c21..00000000 --- a/client/src/app/retreat/postcard/page.tsx +++ /dev/null @@ -1,181 +0,0 @@ -"use client" - -import { get } from "@/config/api" -import Image from "next/image" -import { Box, Stack } from "@mui/material" -import styles from "./page.module.css" -import { useAtom } from "jotai" -import numberToString from "./numberToString" -import { RetreatAttendAtom } from "@/state/retreat" -import { useEffect, useRef, useState } from "react" - -let startPositionX = 0 -let startPositionY = 0 - -let currentRotateX = 0 -let currentRotateY = 0 - -export default function Postcard() { - const [rotateX, setRotateX] = useState(0) - const [rotateY, setRotateY] = useState(0) - const [isMoving, setIsMoving] = useState(false) - const [backWidth, setBackWidth] = useState(0) - const [backHeight, setBackHeight] = useState(0) - const [isNotMove, setIsNotMove] = useState(true) - const [retreatAttend, setRetreatAttend] = useAtom(RetreatAttendAtom) - - const imageRef = useRef(null) - - useEffect(() => { - function touchStart(e: TouchEvent) { - startPositionX = e.touches[0].clientX + currentRotateY * 2 - startPositionY = e.touches[0].clientY + currentRotateX * 2 - - setIsMoving(true) - setIsNotMove(false) - } - addEventListener("touchstart", touchStart) - - function touchMove(e: TouchEvent) { - const diffX = e.touches[0].clientX - startPositionX - const diffY = e.touches[0].clientY - startPositionY - - currentRotateX = diffY * -0.5 - currentRotateY = diffX * 0.5 - - const isReverse = Math.floor((currentRotateX + 90) / 180) % 2 !== 0 - //뒤집혀 있으면 죄우로 긁었을때, 반대로 움직여야 함 - if (isReverse) { - currentRotateY = currentRotateY * -1 - } - - setRotateX(currentRotateX) - setRotateY(currentRotateY) - } - addEventListener("touchmove", touchMove) - - function touchEnd() { - setIsMoving(false) - const tempRotateX = Math.round(currentRotateX / 180.0) - currentRotateX = tempRotateX * 180 - setRotateX(currentRotateX) - - const tempRotateY = Math.round(currentRotateY / 180.0) - currentRotateY = tempRotateY * 180 - setRotateY(currentRotateY) - } - - addEventListener("touchend", touchEnd) - return () => { - removeEventListener("touchstart", touchStart) - removeEventListener("touchmove", touchMove) - removeEventListener("touchend", touchEnd) - } - }, []) - - useEffect(() => { - ;(async () => { - if (imageRef.current) { - // 이미지가 로드되면 뒷면의 크기를 설정 - while (imageRef.current.clientHeight === 0) { - await new Promise((resolve) => setTimeout(resolve, 100)) - } - setBackWidth(imageRef.current.clientHeight) - setBackHeight(imageRef.current.clientWidth) - } - })() - }, [imageRef.current]) - - function CardContent() { - return ( - <> - {retreatAttend?.user?.name}님 수련회 신청이 완료되었습니다~!
- 수련회를 기대하고 기도하는 마음으로
- 같이 준비하면 좋을 거 같아요. -
- 기도로 준비하는 수련회에
- 더 큰 은혜가 있는 건 확실하니까요 :) -
새벽이슬도 당신을 위해 기도하겠습니다! -
수련회 때 만나요 ♥︎ - - ) - } - - return ( - - - - - - {retreatAttend?.user?.name}님에게 보내는{" "} - {numberToString(retreatAttend?.attendanceNumber)}번째 편지 - - - - - - - - ) -} diff --git a/client/src/app/retreat/postcard/preview/page.module.css b/client/src/app/retreat/postcard/preview/page.module.css deleted file mode 100644 index 3a5fcb2a..00000000 --- a/client/src/app/retreat/postcard/preview/page.module.css +++ /dev/null @@ -1,54 +0,0 @@ -.postcard { - width: fit-content; - height: fit-content; - transform-style: preserve-3d; - border: 1px solid #ddd; - - transition: all 0.5s linear; -} - -.postcard-image { - width: calc(100vw - 70px); - object-fit: contain; - height: auto; -} - -.postcard-back { - width: 90%; - height: 90%; - background-image: url(/postcard_back.png); - background-color: #f2e8de; - background-size: cover; - background-position: center; - background-repeat: no-repeat; -} - -.card-content { - margin-top: 40%; - margin-left: 10%; - margin-right: 10%; - width: 80%; - height: 70%; - padding: 20px; - box-sizing: border-box; -} - -.moving-animation { - animation: moving 4s infinite; - animation-direction: initial; -} - -@keyframes moving { - 0% { - transform: rotateY(0deg) rotateX(0deg); - } - 33% { - transform: rotateY(10deg) rotateX(20deg); - } - 66% { - transform: rotateY(-10deg) rotateX(-20deg); - } - 100% { - transform: rotateY(0deg) rotateX(0deg); - } -} diff --git a/client/src/app/retreat/postcard/preview/page.tsx b/client/src/app/retreat/postcard/preview/page.tsx deleted file mode 100644 index 43959a1b..00000000 --- a/client/src/app/retreat/postcard/preview/page.tsx +++ /dev/null @@ -1,91 +0,0 @@ -"use client" - -import { Button, Stack } from "@mui/material" -import styles from "./page.module.css" -import { useRouter } from "next/navigation" -import { useEffect, useRef, useState } from "react" - -export default function Postcard() { - const { push } = useRouter() - const [cardContentText, setCardContentText] = useState("") - - useEffect(() => { - const data = localStorage.getItem("postcardData") - if (data) { - const parsedData = JSON.parse(data) - if (parsedData.text) { - setCardContentText(parsedData.text) - } - } - }, []) - - const imageRef = useRef(null) - useEffect(() => { - ;(async () => { - if (imageRef.current) { - // 이미지가 로드되면 뒷면의 크기를 설정 - while (imageRef.current.clientHeight === 0) { - await new Promise((resolve) => setTimeout(resolve, 100)) - } - console.log( - "imageRef.current.clientWidth", - imageRef.current.clientWidth, - ) - console.log( - "imageRef.current.clientHeight", - imageRef.current.clientHeight, - ) - } - })() - }, [imageRef.current]) - - function CardContent() { - return ( - - {cardContentText.split("\n").map((line, index) => ( - - {line} -
-
- ))} -
- ) - } - - return ( - - - - - - - ) -} diff --git a/client/src/app/retreat/register/page.tsx b/client/src/app/retreat/register/page.tsx new file mode 100644 index 00000000..54118ad0 --- /dev/null +++ b/client/src/app/retreat/register/page.tsx @@ -0,0 +1,105 @@ +"use client" + +import { Box, Button, IconButton, Stack } from "@mui/material" +import ArrowBackIosNewRoundedIcon from "@mui/icons-material/ArrowBackIosNewRounded" +import { useRouter } from "next/navigation" +import { useState } from "react" +import axios from "@/config/axios" +import { useNotification } from "@/hooks/useNotification" + +export default function RetreatRegister() { + const { push } = useRouter() + const { success } = useNotification() + const [isWorking, setIsWorking] = useState(false) + + function handleGoToBack() { + push("/retreat") + } + + async function handleSubmit() { + const { data } = await axios.post("/retreat/attend", { + isWorker: isWorking, + }) + success(data.result) + push(`/retreat/complete?isWorker=${isWorking}`) + } + + return ( + + + + + + 수련회 신청 + + + + 직장인이신가요? + + + + { + setIsWorking(true) + }} + > + 직장인이에요! + + { + setIsWorking(false) + }} + > + 직장인 아니에요! + + + + + + + ) +} diff --git a/client/src/app/retreat/sections/first.tsx b/client/src/app/retreat/sections/first.tsx new file mode 100644 index 00000000..09cf50c8 --- /dev/null +++ b/client/src/app/retreat/sections/first.tsx @@ -0,0 +1,100 @@ +"use client" + +import { Stack } from "@mui/material" + +export default function RetreatMainFirst() { + return ( + + Main Background + + 2026 SUMMER RETREAT + SUWONJEILYOUNGPEOPLE + + + Calling + + + Date + + + + + + + + + + ) +} diff --git a/client/src/app/retreat/sections/fourth.tsx b/client/src/app/retreat/sections/fourth.tsx new file mode 100644 index 00000000..3e0e11d5 --- /dev/null +++ b/client/src/app/retreat/sections/fourth.tsx @@ -0,0 +1,128 @@ +"use client" + +import { useEffect, useRef, useState } from "react" +import { Box, Stack } from "@mui/material" + +const DAY_MS = 1000 * 60 * 60 * 24 + +function getTargetDday() { + const targetDate = new Date(2026, 7, 14) + const today = new Date() + + targetDate.setHours(0, 0, 0, 0) + today.setHours(0, 0, 0, 0) + + return Math.max( + 0, + Math.round((targetDate.getTime() - today.getTime()) / DAY_MS), + ) +} + +export default function RetreatMainFourth() { + const sectionRef = useRef(null) + const animationFrameRef = useRef(null) + const hasAnimatedRef = useRef(false) + const targetDday = getTargetDday() + const [displayedDday, setDisplayedDday] = useState(50) + + const animateDday = () => { + if (hasAnimatedRef.current) { + return + } + + hasAnimatedRef.current = true + + const startValue = 50 + const endValue = targetDday + const duration = 4000 + const startTime = performance.now() + + const step = (time: number) => { + const elapsed = time - startTime + const progress = Math.min(elapsed / duration, 1) + const easedProgress = 1 - Math.pow(1 - progress, 3) + const nextValue = Math.round( + startValue - (startValue - endValue) * easedProgress, + ) + + setDisplayedDday(nextValue) + + if (progress < 1) { + animationFrameRef.current = requestAnimationFrame(step) + } + } + + animationFrameRef.current = requestAnimationFrame(step) + } + + useEffect(() => { + const element = sectionRef.current + + if (!element) { + return + } + + const observer = new IntersectionObserver( + (entries) => { + if (entries.some((entry) => entry.isIntersecting)) { + animateDday() + } + }, + { + threshold: 0.35, + rootMargin: "0px 0px -10% 0px", + }, + ) + + observer.observe(element) + + return () => { + if (animationFrameRef.current !== null) { + cancelAnimationFrame(animationFrameRef.current) + } + + observer.disconnect() + } + }, [targetDday]) + + return ( + + Content + + Calendar + + {`D-${displayedDday}`} + + + + place + + + ) +} diff --git a/client/src/app/retreat/sections/second.tsx b/client/src/app/retreat/sections/second.tsx new file mode 100644 index 00000000..772f8639 --- /dev/null +++ b/client/src/app/retreat/sections/second.tsx @@ -0,0 +1,82 @@ +"use client" + +import { useEffect, useRef, useState } from "react" +import { Stack } from "@mui/material" + +export default function RetreatMainSecond() { + const imageRefs = useRef>([]) + const [visibleIndexes, setVisibleIndexes] = useState([]) + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + setVisibleIndexes((currentVisible) => { + const nextVisible = new Set(currentVisible) + + entries.forEach((entry) => { + const index = Number(entry.target.getAttribute("data-index")) + + if (entry.isIntersecting) { + nextVisible.add(index) + } + }) + + return Array.from(nextVisible).sort((left, right) => left - right) + }) + }, + { + threshold: 0.35, + rootMargin: "0px 0px -12% 0px", + }, + ) + + imageRefs.current.forEach((element) => { + if (element) { + observer.observe(element) + } + }) + + return () => { + observer.disconnect() + } + }, []) + + return ( + + {[ + { + src: "/retreat/main/second/invitation.png", + alt: "Invitation", + }, + { + src: "/retreat/main/second/content.png", + alt: "Content", + }, + ].map((item, index) => ( + { + imageRefs.current[index] = element + }} + data-index={index} + src={item.src} + alt={item.alt} + width="80%" + style={{ + opacity: visibleIndexes.includes(index) ? 1 : 0, + transform: visibleIndexes.includes(index) + ? "translateY(0)" + : "translateY(24px)", + transition: "opacity 0.8s ease, transform 0.8s ease", + }} + /> + ))} + + ) +} diff --git a/client/src/app/retreat/sections/third.tsx b/client/src/app/retreat/sections/third.tsx new file mode 100644 index 00000000..33034f3a --- /dev/null +++ b/client/src/app/retreat/sections/third.tsx @@ -0,0 +1,202 @@ +"use client" + +import { useEffect, useRef, useState } from "react" +import { Stack } from "@mui/material" + +export default function RetreatMainThird() { + const thumbWidth = 25 + const imageRefs = useRef>([]) + const carouselRef = useRef(null) + const [visibleIndexes, setVisibleIndexes] = useState([]) + const [scrollProgress, setScrollProgress] = useState(0) + + const updateScrollIndicator = () => { + const element = carouselRef.current + + if (!element) { + return + } + + const maxScrollLeft = element.scrollWidth - element.clientWidth + + if (maxScrollLeft <= 0) { + setScrollProgress(0) + return + } + + setScrollProgress(element.scrollLeft / maxScrollLeft) + } + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + setVisibleIndexes((currentVisible) => { + const nextVisible = new Set(currentVisible) + + entries.forEach((entry) => { + const index = Number(entry.target.getAttribute("data-index")) + + if (entry.isIntersecting) { + nextVisible.add(index) + } + }) + + return Array.from(nextVisible).sort((left, right) => left - right) + }) + }, + { + threshold: 0.35, + rootMargin: "0px 0px -12% 0px", + }, + ) + + imageRefs.current.forEach((element) => { + if (element) { + observer.observe(element) + } + }) + + updateScrollIndicator() + window.addEventListener("resize", updateScrollIndicator) + + return () => { + observer.disconnect() + window.removeEventListener("resize", updateScrollIndicator) + } + }, []) + + return ( + + {[ + { + src: "/retreat/main/third/timetable.png", + alt: "Timetable", + }, + ].map((item, index) => ( + { + imageRefs.current[index] = element + }} + data-index={index} + src={item.src} + alt={item.alt} + width="80%" + onLoad={updateScrollIndicator} + style={{ + opacity: visibleIndexes.includes(index) ? 1 : 0, + transform: visibleIndexes.includes(index) + ? "translateY(0)" + : "translateY(24px)", + transition: "opacity 0.8s ease, transform 0.8s ease", + }} + /> + ))} + + Notice + Notice + Notice + Notice + + +
+
+
+ + + ) +} diff --git a/client/src/app/retreat/steps/fifthStep.tsx b/client/src/app/retreat/steps/fifthStep.tsx deleted file mode 100644 index 1f60dc15..00000000 --- a/client/src/app/retreat/steps/fifthStep.tsx +++ /dev/null @@ -1,65 +0,0 @@ -"use client" - -import { Stack, Box } from "@mui/material" -import useRetreat from "../hooks/useRetreat" -import RetreatButton from "../components/Button" -import { useNotification } from "@/hooks/useNotification" - -export default function FifthStep() { - const { isWorker, isHalf } = useRetreat() - const { success } = useNotification() - - function calculateRetreatFee() { - if (isHalf) { - return "10만원" - } - if (isWorker) { - return "15만원" - } else { - return "12만원" - } - } - - return ( - - - - 수련회 신청이 완료 되었습니다. - - - 수련회비는
- 아래 계좌로 입금해 주세요. -
- - 3333342703455
- 카카오뱅크 성은비 -
- 수련회비 {calculateRetreatFee()} -
- - - { - navigator.clipboard.writeText( - "3333342703455 카카오뱅크 성은비 " + calculateRetreatFee(), - ) - success("계좌 정보가 복사되었습니다.") - }} - /> - - -
- ) -} diff --git a/client/src/app/retreat/steps/first.tsx b/client/src/app/retreat/steps/first.tsx deleted file mode 100644 index f7d5897c..00000000 --- a/client/src/app/retreat/steps/first.tsx +++ /dev/null @@ -1,165 +0,0 @@ -"use client" - -import { Box, Stack } from "@mui/material" -import useRetreat from "../hooks/useRetreat" -import RetreatButton from "../components/Button" -import { useEffect, useState } from "react" -import useAuth from "@/hooks/useAuth" -import { useRouter } from "next/navigation" - -export default function FirstStep() { - const [name, setName] = useState("") - const [birthYear, setBirthYear] = useState("") - const [phone, setPhone] = useState("010 - - ") - - const { kakaoToken, isLogin } = useAuth() - const { setStep, updateNuon, getMyInfo } = useRetreat() - const { push } = useRouter() - - useEffect(() => { - if (!isLogin) return - getMyInfo().then((data) => { - setName(data.name) - setBirthYear(data.yearOfBirth.toString()) - setPhone( - data.phone.slice(0, 3) + - "-" + - data.phone.slice(3, 7) + - "-" + - data.phone.slice(7, 11), - ) - }) - }, [isLogin]) - - async function handleNextStep(gender: "man" | "woman") { - if (!kakaoToken) { - alert("로그인이 필요합니다.") - push("/retreat/login") - return - } - if (name.trim() === "" || birthYear.trim() === "" || phone.trim() === "") { - alert("모든 정보를 입력해주세요.") - return - } - try { - await updateNuon({ - kakaoToken: kakaoToken, - name, - yearOfBirth: parseInt(birthYear), - phone: phone.replaceAll("-", ""), - gender: gender, - }) - } catch (e) { - alert( - "정보 등록에 실패했습니다. 새로고침후 다시 시도해주세요." + - e.toString(), - ) - return - } - setStep(2) - } - - function onClickPhone() { - if (phone === "010 - - ") { - setPhone("010-") - } - } - - return ( - - - - 등록된 정보가 없어 입력이 필요합니다. - - 아래 정보를 입력해주세요. - - - {}} - /> - setName(e.target.value)} - style={{ - position: "absolute", - width: "60%", - height: "40px", - background: "transparent", - border: "none", - outline: "none", - color: "white", - fontSize: "16px", - paddingLeft: "80px", - }} - /> - - - {}} - /> - setBirthYear(e.target.value)} - style={{ - textAlign: "center", - position: "absolute", - width: "80%", - height: "40px", - background: "transparent", - border: "none", - outline: "none", - color: "white", - fontSize: "16px", - }} - /> - - - - setPhone(e.target.value)} - maxLength={17} - style={{ - textAlign: "center", - position: "absolute", - width: "60%", - height: "40px", - background: "transparent", - border: "none", - outline: "none", - color: "white", - fontSize: "16px", - paddingLeft: "20px", - }} - /> - - - handleNextStep("man")} /> - handleNextStep("woman")} /> - - - ) -} diff --git a/client/src/app/retreat/steps/fourth.tsx b/client/src/app/retreat/steps/fourth.tsx deleted file mode 100644 index 820f68cb..00000000 --- a/client/src/app/retreat/steps/fourth.tsx +++ /dev/null @@ -1,114 +0,0 @@ -"use client" - -import { Stack, Box } from "@mui/material" -import useRetreat from "../hooks/useRetreat" -import RetreatButton from "../components/Button" -import { useEffect, useState } from "react" -import { User } from "@server/entity/user" -import { useNotification } from "@/hooks/useNotification" - -export default function FourthStep() { - const [myInfo, setMyInfo] = useState(null) - const { saveRetreatAttend, getMyInfo, isWorker, isHalf, setStep } = - useRetreat() - const { error, success } = useNotification() - - useEffect(() => { - getMyInfo().then((data) => { - setMyInfo(data) - }) - }, []) - - function getGenderString() { - const gender = myInfo?.gender - if (!gender) return "" - if (gender === "man") { - return "남자" - } else { - return "여자" - } - } - - async function saveRetreatAttendWrapper() { - try { - const { data } = await saveRetreatAttend() - success(data.result) - setStep(5) - } catch (e) { - error( - "참가 신청에 실패했습니다. 새로고침 후 다시 시도해주세요." + - e.toString(), - ) - } - } - - return ( - - - - 입력해주신 내용을 토대로 정리했습니다. - - - 아래 내용이 맞는지 -
- 다시 한 번 확인해 주세요. -
-
- - - {}} /> - {}} - /> - - - - {}} /> - - - {}} /> - - - - - {}} - /> - - - {}} - /> - - - - - - { - setStep(1) - }} - /> - - -
- ) -} diff --git a/client/src/app/retreat/steps/second.tsx b/client/src/app/retreat/steps/second.tsx deleted file mode 100644 index 31599f12..00000000 --- a/client/src/app/retreat/steps/second.tsx +++ /dev/null @@ -1,47 +0,0 @@ -"use client" - -import { Box, Stack } from "@mui/material" -import RetreatButton from "../components/Button" -import useRetreat from "../hooks/useRetreat" - -export default function SecondStep() { - const { setStep, setIsHalf } = useRetreat() - - async function handleNextStep(isHalf: boolean) { - setStep(3) - setIsHalf(isHalf) - } - return ( - - - - (1/30 ~ 2/01) - - - 2026 겨울 수련회 -
- 참석일시를 선택해주세요. -
- - *토요일 저녁이후 참석자만 -
- 부분참석으로 분류되기 때문에 하는 조사합니다. -
-
- - { - handleNextStep(false) - }} - /> - { - handleNextStep(true) - }} - /> - -
- ) -} diff --git a/client/src/app/retreat/steps/third.tsx b/client/src/app/retreat/steps/third.tsx deleted file mode 100644 index 23493041..00000000 --- a/client/src/app/retreat/steps/third.tsx +++ /dev/null @@ -1,42 +0,0 @@ -"use client" - -import { Stack, Box } from "@mui/material" -import RetreatButton from "../components/Button" -import useRetreat from "../hooks/useRetreat" - -export default function ThirdStep() { - const { setIsWorker, setStep } = useRetreat() - - function handleNextStep(isWorker: boolean) { - setIsWorker(isWorker) - setStep(4) - } - - return ( - - - - 수련회비를 위한 항목 입니다. - - - 아래 2개의 항목 중
- 해당하는 항목을 선택 해 주세요. -
-
- - { - handleNextStep(true) - }} - /> - { - handleNextStep(false) - }} - /> - -
- ) -} diff --git a/client/src/app/temp-app/page.tsx b/client/src/app/temp-app/page.tsx deleted file mode 100644 index 3f2e7d69..00000000 --- a/client/src/app/temp-app/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import Header from "@/components/Header" -import Link from "../link/page" - -export default function Page() { - return ( -
-
- -
- ) -} diff --git a/server/src/routes/retreat/retreatRouter.ts b/server/src/routes/retreat/retreatRouter.ts index 20859f3b..740f1866 100644 --- a/server/src/routes/retreat/retreatRouter.ts +++ b/server/src/routes/retreat/retreatRouter.ts @@ -124,19 +124,19 @@ router.post("/attend", async (req, res) => { }) if (foundRetreatAttend) { - foundRetreatAttend.isHalf = req.body.isHalf + //foundRetreatAttend.isHalf = req.body.isHalf foundRetreatAttend.isWorker = req.body.isWorker await retreatAttendDatabase.save(foundRetreatAttend) res.send({ result: "수련회 정보가 수정 되었습니다." }) return } - const { isHalf, isWorker } = req.body + const { isWorker } = req.body const retreatAttend = retreatAttendDatabase.create({ user: { id: foundUser.id, }, - isHalf: isHalf, + isHalf: false, isWorker: isWorker, }) retreatAttend.attendanceNumber = (await retreatAttendDatabase.count()) + 1 @@ -144,108 +144,6 @@ router.post("/attend", async (req, res) => { res.send({ result: "수련회 정보가 등록 되었습니다." }) }) -router.post("/set-postcard-content", async (req, res) => { - const foundUser = await getUserFromToken(req) - - if (!foundUser) { - res.status(401).send({ result: "fail" }) - return - } - - const { content, targetUserId } = req.body - - const foundRetreatAttend = await retreatAttendDatabase.findOne({ - where: { - user: { - id: targetUserId, - }, - }, - relations: { - user: true, - }, - }) - - if (foundRetreatAttend) { - foundRetreatAttend.postcardContent = content - await retreatAttendDatabase.save(foundRetreatAttend) - res.send({ result: "success" }) - return - } - - const community = await communityDatabase.findOne({ - where: { - users: { - id: targetUserId, - }, - }, - relations: { - users: true, - leader: true, - deputyLeader: true, - }, - }) - - if (!community) { - res.status(404).send({ result: "fail", message: "Community not found" }) - return - } - - if (!community.leader || !community.deputyLeader) { - res - .status(403) - .send({ result: "fail", message: "Leader or deputy leader not found" }) - return - } - - if ( - community.leader.id !== foundUser.id && - community.deputyLeader.id !== foundUser.id - ) { - res.status(403).send({ result: "fail", message: "Permission denied" }) - return - } - const newRetreatAttendData = retreatAttendDatabase.create({ - user: { - id: targetUserId, - }, - postcardContent: content, - }) - - await retreatAttendDatabase.save(newRetreatAttendData) - res.send({ result: "success" }) -}) - -router.get("/get-postcard-content/:targetUserId", async (req, res) => { - const foundUser = await getUserFromToken(req) - - if (!foundUser) { - res.status(401).send({ result: "fail" }) - return - } - - const targetUserId = req.params.targetUserId - - const foundRetreatAttend = await retreatAttendDatabase.findOne({ - where: { - user: { - id: targetUserId, - }, - }, - relations: { - user: true, - }, - }) - - if (foundRetreatAttend) { - res.send({ result: "success", content: foundRetreatAttend.postcardContent }) - return - } - - res - .status(404) - .send({ result: "fail", message: "Postcard content not found" }) -}) - router.use("/sharing", sharingRouter) router.use("/admin", adminRouter)