diff --git a/package-lock.json b/package-lock.json index 08a26c9..2bff11e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", + "@hugocxl/react-to-image": "^0.0.9", "@tanstack/react-query": "^5.51.11", "axios": "^1.7.2", "framer-motion": "^11.3.8", @@ -1794,6 +1795,16 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@hugocxl/react-to-image": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@hugocxl/react-to-image/-/react-to-image-0.0.9.tgz", + "integrity": "sha512-UzPtjPb5k0V8oPKjmDvYnWtTNCuFh+2ysXF4+dXL0tnEaFDfu2M3iSt32pzKbJsZYoFu5X12JKKd9MKa2OsR6g==", + "license": "MIT", + "peerDependencies": { + "html-to-image": ">=1", + "react": ">=16" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -4848,6 +4859,13 @@ "react-is": "^16.7.0" } }, + "node_modules/html-to-image": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz", + "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==", + "license": "MIT", + "peer": true + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", diff --git a/package.json b/package.json index fca1453..3586a2d 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", + "@hugocxl/react-to-image": "^0.0.9", "@tanstack/react-query": "^5.51.11", "axios": "^1.7.2", "framer-motion": "^11.3.8", diff --git a/public/bg1.jpg b/public/bg1.jpg new file mode 100644 index 0000000..534d947 Binary files /dev/null and b/public/bg1.jpg differ diff --git a/public/bg2.jpg b/public/bg2.jpg new file mode 100644 index 0000000..4f86b87 Binary files /dev/null and b/public/bg2.jpg differ diff --git a/public/bg3.jpg b/public/bg3.jpg new file mode 100644 index 0000000..1215149 Binary files /dev/null and b/public/bg3.jpg differ diff --git a/public/bg4.jpg b/public/bg4.jpg new file mode 100644 index 0000000..0549876 Binary files /dev/null and b/public/bg4.jpg differ diff --git a/src/app/letter/@create/page.tsx b/src/app/letter/@create/page.tsx index 0704b1b..2266abf 100644 --- a/src/app/letter/@create/page.tsx +++ b/src/app/letter/@create/page.tsx @@ -23,13 +23,19 @@ import { useSourceStore } from '../letter-source-store'; import { useLetterStore } from '../letter-store'; import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'; import { useState, useRef } from 'react'; +import { useLetterFlowStore } from '../letter-flow-store'; const Page = () => { const { isOpen, onOpen, onClose } = useDisclosure(); const cancelRef = useRef(null); const { source } = useSourceStore(); - const { letter } = useLetterStore(); - const [currentLetterIdx, setCurrentLetterIdx] = useState(0); + const { + letter, + currentLetterIndex, + increaseCurrentLetterIndex, + decreaseCurrentLetterIndex, + } = useLetterStore(); + const { setState } = useLetterFlowStore(); // TODO : 저장 개수 + 현재 불러올 수 있는 편지 개수 받아와야 함. const DUMMYSAVECOUNT = 2; const DUMMYLOADCOUNT = 3; @@ -50,7 +56,11 @@ const Page = () => { - } /> + } + /> { - } /> + } + /> From. @@ -71,7 +85,7 @@ const Page = () => { - + { + const { isOpen, onOpen, onClose } = useDisclosure(); + const openButtonRef = useRef(null); + const { setState } = useLetterFlowStore(); + const { letter, currentLetterIndex } = useLetterStore(); + const [imageSrc, setImageSrc] = useState(); + const [{ status, isLoading }, convertToImage, ref] = useToPng( + { + onStart: () => { + console.log('onStart'); + }, + onSuccess: (data) => { + setImageSrc(data); + }, + onError: (error) => { + console.error(error); + }, + } + ); + + const [fontWeight, setFontWeight] = useState('normal'); + const [fontSize, setFontSize] = useState(16); + const [date, setDate] = useState( + new Date().toISOString().split('T')[0].split('-').join('-') + ); + const [showDate, setShowDate] = useState(false); + const [bgImage, setBgImage] = useState('bg1'); + + return ( + <> + + {/* 편지 이미지 컴포넌트 */} + + {letter[currentLetterIndex].title} + {`To. ${letter[currentLetterIndex].to}`} + + {letter[currentLetterIndex].body.split('\n').map((line, index) => ( + + {line} + + ))} + + + {`From. ${letter[currentLetterIndex].from}`} + {showDate && {`Date. ${date}`}} + + + + + + + + } + /> + + + + + + + 텍스트 + + { + setFontSize(valueAsNumber); + }} + > + + + + + + + + + + 텍스트 + { + setShowDate(!showDate); + }} + > + 날짜 표시하기 + + { + setDate(event.target.value); + }} + /> + + + 편지지 + + + + + + + + + + + + + + + + + + ); +}; + +export default Page; diff --git a/src/app/letter/layout.tsx b/src/app/letter/layout.tsx index d859273..718f077 100644 --- a/src/app/letter/layout.tsx +++ b/src/app/letter/layout.tsx @@ -7,9 +7,10 @@ type LayoutProps = { children: ReactNode; // 기본 페이지 initial: ReactNode; // 초기 페이지 create: ReactNode; // 편지 쓰기 페이지 + decorate: ReactNode; // 편지 꾸미기 페이지 }; -const Layout = ({ children, initial, create }: LayoutProps) => { +const Layout = ({ children, initial, create, decorate }: LayoutProps) => { const { state } = useLetterFlowStore(); if (state === 'initial') { return <>{initial}; @@ -17,6 +18,10 @@ const Layout = ({ children, initial, create }: LayoutProps) => { if (state === 'create') { return <>{create}; } + if (state === 'decorate') { + return <>{decorate}; + } + return <>{children}; }; diff --git a/src/app/letter/letter-store.ts b/src/app/letter/letter-store.ts index dd0a36c..4c163c3 100644 --- a/src/app/letter/letter-store.ts +++ b/src/app/letter/letter-store.ts @@ -9,13 +9,37 @@ export type LetterData = { type LetterStore = { letter: LetterData[]; + currentLetterIndex: number; addNewLetter: (letter: LetterData) => void; + decreaseCurrentLetterIndex: () => void; + increaseCurrentLetterIndex: () => void; }; -const defaultState: LetterData[] = []; +const defaultState: LetterData[] = [ + { + title: '임시 저장', + to: '개발팀', + from: '기획팀', + body: '사전 질문의 답변으로 사용자 문체를 파악하고 아키네이터가 질문지 답변을 바탕으로 내용을 구성하여 편지 1을 보여줍니다. 그리고 사용자가 후속으로 직접 수정할 수 있으며 완성된 편지는 사용자 데이터로 남아 기존의 사용자 데이터 + 아카이빙 데이터 형식으로 누적됩니다.', + }, +]; +const defaultIndex = 0; export const useLetterStore = create((set) => ({ letter: defaultState, + currentLetterIndex: defaultIndex, addNewLetter: (letter) => set((state) => ({ letter: [...state.letter, letter] })), + decreaseCurrentLetterIndex: () => + set((state) => ({ + currentLetterIndex: + state.currentLetterIndex - 1 > 0 ? state.currentLetterIndex - 1 : 0, + })), + increaseCurrentLetterIndex: () => + set((state) => ({ + currentLetterIndex: + state.currentLetterIndex + 1 < state.letter.length + ? state.currentLetterIndex + 1 + : state.letter.length - 1, + })), }));