diff --git a/src/app/(learn)/educations/[id]/page.tsx b/src/app/(learn)/educations/[id]/page.tsx index 90bacf3..ada1cb4 100644 --- a/src/app/(learn)/educations/[id]/page.tsx +++ b/src/app/(learn)/educations/[id]/page.tsx @@ -5,25 +5,56 @@ import EducationContent from '@/components/molecules/Education/EducationContent' import ContentsEditBtn from '@/components/molecules/manage/ContentsEditBtn'; import ContentsDeleteBtn from '@/components/molecules/manage/ContentsDeleteBtn'; import ManageBtns from '@/components/molecules/manage/ManageBtns'; -import { TEducation } from '@/components/molecules/Education/EducationList'; -import { useEffect, useState } from 'react'; -import { getEducationIdApi } from '@/api/education/educationDetailApi'; - -import { testApi, testApiEditor } from '@/api/testApi'; import useEduDetail from '@/hooks/useEduDetail'; import { deleteEduApi, patchEduApi } from '@/api/eduApi'; +import { postEducationBookmarkApi, deleteEducationBookmarkApi } from '@/api/bookmarkApi'; +import WithLoginModal from '@/components/templates/login/WithLoginModal'; +import { useEffect, useState } from 'react'; const Education = ({ params }: { params: { id: number } }) => { const { eduData } = useEduDetail(params.id); + const [isLiked, setIsLiked] = useState(false); + const [showModal, setShowModal] = useState(false); + + useEffect(() => { + if (eduData) { + setIsLiked(eduData.bookmarked); + } + //eslint-disable-next-line react-hooks/exhaustive-deps + }, [eduData]); + + const onHeartClick = async (id: number, isLiked: boolean | undefined) => { + try { + let apiResult; + if (isLiked) { + apiResult = await deleteEducationBookmarkApi(id, 'EDU_CONTENT'); + } else { + apiResult = await postEducationBookmarkApi(id, 'EDU_CONTENT'); + } + if (apiResult !== undefined) { + setIsLiked(!isLiked); + } else { + setShowModal(true); + } + } catch (error) { + console.error('Error fetching bankBookmark:', error); + } + }; - const onHeartClick = async (id: number, bookmarked: boolean, contentType: 'EDU_CONTENT') => {}; return eduData ? ( -
+
+ {showModal && ( + { + setShowModal(false); + }} + /> + )}
onHeartClick(params.id, eduData.bookmarked, 'EDU_CONTENT')} + bookmarked={isLiked} + onHeartClick={() => onHeartClick(params.id, isLiked)} /> {eduData && } diff --git a/src/app/(learn)/educations/page.tsx b/src/app/(learn)/educations/page.tsx index df3aeec..6b425c3 100644 --- a/src/app/(learn)/educations/page.tsx +++ b/src/app/(learn)/educations/page.tsx @@ -1,14 +1,11 @@ 'use client'; import React, { useState } from 'react'; -import Link from 'next/link'; import Education from '@/components/molecules/Education/EducationList'; import NewsList from '@/components/molecules/News/NewsList'; import StudyToggle2 from '@/components/atom/toggle/StudyToggle2'; import ManageBtns from '@/components/molecules/manage/ManageBtns'; import ContentsCreateBtn from '@/components/molecules/manage/ContentsCreateBtn'; -import { testApiEditor } from '@/api/testApi'; -import useUser from '@/hooks/useUser'; -import { postNewsApi } from '@/api/newsApi'; + import { postEduApi } from '@/api/eduApi'; const Educations: any = () => { diff --git a/src/components/molecules/Education/EducationContent.tsx b/src/components/molecules/Education/EducationContent.tsx index f0334af..9bec1c8 100644 --- a/src/components/molecules/Education/EducationContent.tsx +++ b/src/components/molecules/Education/EducationContent.tsx @@ -1,15 +1,17 @@ 'use client'; + import EditorRenderer from '@/components/templates/editor/EditorRenderer'; -import { useSearchParams } from 'next/navigation'; + type TEducationContentProps = { content: string; }; + const EducationContent: React.FC = ({ content }) => { return (
-
-
-
+
+
+
diff --git a/src/components/molecules/Education/EducationHeadLine.tsx b/src/components/molecules/Education/EducationHeadLine.tsx index f954b58..25a2229 100644 --- a/src/components/molecules/Education/EducationHeadLine.tsx +++ b/src/components/molecules/Education/EducationHeadLine.tsx @@ -1,10 +1,8 @@ 'use client'; -import { useSearchParams } from 'next/navigation'; import Heartdefault from '../../../public/icons/grayheart2.svg'; import Glasses_goldtorihalf from '../../../public/icons/glasses_goldtorihalf.svg'; import Heartclick from '@/public/icons/clickheart2.svg'; -import { useState } from 'react'; type TEducationHeadLineProps = { title: string; @@ -14,20 +12,24 @@ type TEducationHeadLineProps = { const EducationHeadLine: React.FC = ({ title, bookmarked, onHeartClick }) => { return ( -
-
- -
-
-
+
+ +
+
{title}
-

{ + event.stopPropagation(); + onHeartClick(); + }} > - {bookmarked ? : } -

+ {bookmarked ? ( + + ) : ( + + )} +
); diff --git a/src/components/molecules/Education/EducationList.tsx b/src/components/molecules/Education/EducationList.tsx index 0215e3c..7b92a95 100644 --- a/src/components/molecules/Education/EducationList.tsx +++ b/src/components/molecules/Education/EducationList.tsx @@ -1,17 +1,15 @@ 'use client'; -import Link from 'next/link'; // Education 컴포넌트 import React, { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; import Heartdefault from '../../../public/icons/grayheart2.svg'; import ModifiedGlasses_goldtori from '../../../public/icons/modifiedglasses_goldtori.svg'; import Clickheart2 from '@/public/icons/clickheart2.svg'; import { getEducationsData } from '@/api/education/educationApi'; import Pagination from '@/components/molecules/pagination/Pagination'; import { deleteEducationBookmarkApi, postEducationBookmarkApi } from '@/api/bookmarkApi'; - -import useUser from '@/hooks/useUser'; import SlateCompiler from '@/libs/editor/slateCompiler'; -import { user } from '@/class/user'; +import WithLoginModal from '@/components/templates/login/WithLoginModal'; import truncateText from '@/utils/truncateText'; export type TEducation = { @@ -49,9 +47,12 @@ export type TEducationsApiResponse = { numberOfElements: number; empty: boolean; }; + const Education = () => { + const router = useRouter(); const slateCompiler = new SlateCompiler(); - const [EducationData, setEducationData] = useState([]); + const [educationData, setEducationData] = useState([]); + const [showModal, setShowModal] = useState(false); //페이지 const [pageNum, setPageNum] = useState(0); //현재 페이지 @@ -69,55 +70,69 @@ const Education = () => { } }; - const onHeartClick = (id: number, bookmarked: boolean) => { - // TODO: 북마크 api 연결 @이가은 + const onHeartClick = async (id: number, bookmarked: boolean) => { + try { + let apiResult; + if (bookmarked) { + apiResult = await deleteEducationBookmarkApi(id, 'EDU_CONTENT'); + } else { + apiResult = await postEducationBookmarkApi(id, 'EDU_CONTENT'); + } + if (apiResult !== undefined) { + setEducationData(educationData?.map((item) => (item.id === id ? { ...item, bookmarked: !bookmarked } : item))); + } else { + setShowModal(true); + } + } catch (error) { + console.error('Error fetching bankBookmark:', error); + } }; useEffect(() => { fetchData(); + //eslint-disable-next-line react-hooks/exhaustive-deps }, [pageNum]); return (
-
- {EducationData?.map((i, index) => ( + {showModal && ( + { + setShowModal(false); + }} + /> + )} +
+ {educationData?.map((i) => (
router.push(`/educations/${i.id}`)} + className='flex-col tablet:ml-20 w-336 my-12 desktop:w-425 tablet:w-371 desktop:my-10 rounded-lg cursor-pointer' > - -
-

- {i.title} -

-
- -
+
+

+ {i.title} +

+
+ +
+
+
+
+ {truncateText(slateCompiler.toPlainText(JSON.parse(i.content)), 50)}
- -
- -
- {truncateText(slateCompiler.toPlainText(JSON.parse(i.content)), 50)} -
-
diff --git a/src/components/molecules/Education/dummydata.json b/src/components/molecules/Education/dummydata.json deleted file mode 100644 index 73d7673..0000000 --- a/src/components/molecules/Education/dummydata.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "title": "자유 입출금 통장 알찬사용법", - "content": "자유 입출금 통장 이름 그대로 돈을 자유롭게 입금하고 출금할 수 있는 통장 자유 입출금 통장의 금리 은행마다 차이는 있지만 대부분 연 0.1%로 거의 미미한 수준이지. 물론 파킹 통장 형식의 입출금 통장의 경우 금리를 더 높게 주기도 해!토리야! 그럼 왜 이렇게 금리가 낮은거야?그건 바로 기간의 정함이 없기 때문이야! 은행은 예금고객 돈으로 대출고객에게 돈을 빌려주면서 이익을 벌고 있어 이런 이익을 예대마진이라고 해. 예대마진은 예금자의 돈이 있어야 가능한데 예금자가 갑자기 돈을 인출한다면 은행에서는 문제가 있을 수 있어. 그래서 예금 잔액의 변동 폭이 큰 자유입출금 통장에는 작은 금리를 부여하기도 해.자유 입출금 통장에서의 특별한 혜택자유입출금통장으로 급여를 받거나 공과금등을 결제하면 이체 수수료 면제등의 혜택이나일부 은행의 경우에는 적금 금리우대나 급여 이체시 추가 포인트(금액), 은행자체 고객등급점수 상향 등의 다양한 혜택을 받을 수 있어!추가로 일부 은행에서는 신규로 계좌개설을 한 고객에게 다양한 경품이나 현금을 지급하기도 해." - }, - { - "title": "교육 콘텐츠 2", - "content": "자유 입출금 통장 이름 그대로 돈을 자유롭게 입금하고 출금할 수 있는 통장이야 자유 입출금 통장의 금리은행이고 이고 입니다." - }, - { - "title": "교육 콘텐츠 3", - "content": "자유 입출금 통장 이름 그대로 돈을 자유롭게 입금하고 출금할 수 있는 통장이야 자유 입출금 통장의 금리은행이고 이고 입니다." - }, - { - "title": "교 4", - "content": "자유 수 있" - }, - { - "title": "교육 콘텐츠 5", - "content": "자유 입출금 통장 이름 그대로 돈을 자유롭게 입금하고 출금할 수 있는 통장이야 자유 입출금 통장의 금리은행이고 이고 입니다." - } -] diff --git a/src/components/molecules/News/NewsList.tsx b/src/components/molecules/News/NewsList.tsx index 9caaab7..c402308 100644 --- a/src/components/molecules/News/NewsList.tsx +++ b/src/components/molecules/News/NewsList.tsx @@ -15,12 +15,11 @@ import { postNoticeApi } from '@/api/noticeApi'; import ContentsCreateBtn from '../manage/ContentsCreateBtn'; import ManageBtns from '../manage/ManageBtns'; import EditorRenderer from '@/components/templates/editor/EditorRenderer'; -import { useRouter } from 'next/navigation'; -import Image from 'next/image'; -import NewsImage from '@/public/icons/newsthumbnail/Untitled.jpeg'; +import SlateCompiler from '@/libs/editor/slateCompiler'; +import truncateText from '@/utils/truncateText'; + const NewsList = () => { - const { user } = useUser(); - const router = useRouter(); + const slateCompiler = new SlateCompiler(); const [NewsListData, setNewsListData] = useState([]); const [showModal, setShowModal] = useState(false); //페이지 @@ -70,51 +69,40 @@ const NewsList = () => { setShowModal(false); }} /> - )}{' '} - {NewsListData?.map((i) => { + )} + {NewsListData?.map((i, index) => { let date = new Date(i.created_at); let dateOnly = date.toISOString().split('T')[0]; return ( -
router.push(`/news/${i.id}`)} + -
- {'뉴스썸네일'} -
-
-
+
+

{i.title}

-
- -
-
- {dateOnly} -
+ {truncateText(slateCompiler.toPlainText(JSON.parse(i.content)), 50)} + +
{dateOnly}
- -

{ event.stopPropagation(); onHeartClick(i.id, i.bookmarked); }} > {i.bookmarked ? : } -

+
-
+ ); })} diff --git a/src/components/molecules/Policy/Policy.tsx b/src/components/molecules/Policy/Policy.tsx index 8a1f08c..e714c15 100644 --- a/src/components/molecules/Policy/Policy.tsx +++ b/src/components/molecules/Policy/Policy.tsx @@ -79,7 +79,7 @@ const Policy = () => { onClick={() => router.push(`/policies/${item.policyInfoId}`)} >
-

+

{item.policyName}

diff --git a/src/components/templates/chatbot/index.tsx b/src/components/templates/chatbot/index.tsx index 5452665..27a52b1 100644 --- a/src/components/templates/chatbot/index.tsx +++ b/src/components/templates/chatbot/index.tsx @@ -39,7 +39,7 @@ export default function Chatbot() { return ( @@ -57,7 +57,7 @@ export default function Chatbot() { {!showChatbot && ( diff --git a/src/components/templates/editor/EditorRenderer.tsx b/src/components/templates/editor/EditorRenderer.tsx index 6cc8982..a014aac 100644 --- a/src/components/templates/editor/EditorRenderer.tsx +++ b/src/components/templates/editor/EditorRenderer.tsx @@ -12,7 +12,7 @@ export default function EditorRenderer({
{props.children}
diff --git a/src/components/templates/editor/editor.tsx b/src/components/templates/editor/editor.tsx index c070f2c..d2b60cc 100644 --- a/src/components/templates/editor/editor.tsx +++ b/src/components/templates/editor/editor.tsx @@ -63,6 +63,7 @@ export default function Editor({ return; } + // const content = JSON.stringify(editorValue); const content = JSON.stringify(editorValue); console.log('title', title); diff --git a/src/libs/editor/slateCompiler.ts b/src/libs/editor/slateCompiler.ts index 0bd0add..01c2c65 100644 --- a/src/libs/editor/slateCompiler.ts +++ b/src/libs/editor/slateCompiler.ts @@ -59,6 +59,8 @@ class SlateCompiler { return `


${children}

`; case 'code-block': return this.highlightCodeBlock(node); + case 'image': + return `${'이미지'}`; case 'link': return `${children}`; @@ -76,6 +78,9 @@ class SlateCompiler { if (Text.isText(node)) { return node.text; } + if (!node.children?.length) { + return ''; + } return node.children.map((n) => this.toPlainTextChild(n)).join(' '); }