Skip to content

Commit

Permalink
✨ Feat - 세미나 상세 노션 api 연결 #44
Browse files Browse the repository at this point in the history
  • Loading branch information
bianbbc87 committed May 7, 2024
1 parent 3601137 commit 84783ec
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 78 deletions.
54 changes: 54 additions & 0 deletions src/app/api/member/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Client } from '@notionhq/client';
import { NextRequest } from 'next/server';

const notion = new Client({
auth: process.env.NOTION_SECRET_KEY,
});

// seminar id를 가진 member 불러오기
async function queryMemberData(databaseId: string, seminarId: string): Promise<any[]> {
try {
const response = await notion.databases.query({
database_id: databaseId,
filter: {
property: 'Seminars',
relation: {
contains: seminarId
}
},
});

return response.results;
} catch (error) {
console.error('Error querying Notion database and fetching member data:', JSON.stringify(error));
throw error;
}
}

type Data = {
items?: any[];
message: string;
};

export async function GET(req: NextRequest) {
const url = new URL(req.url);
const seminarId = url.searchParams.get('seminarId') || ''; // 쿼리 파라미터에서 세미나 ID 가져오기
const databaseId = process.env.NOTION_MEMBER_DATABASE_ID || '';

try {
const data = await queryMemberData(databaseId, seminarId);
return new Response(JSON.stringify({ data, message: 'Success' }), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
return new Response(JSON.stringify({ message: `Failed: ${error?.toString()}` }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}
59 changes: 59 additions & 0 deletions src/app/api/seminar/review/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Client } from '@notionhq/client';
import { NextRequest } from 'next/server';

const notion = new Client({
auth: process.env.NOTION_SECRET_KEY,
});

// seminar id와 연결된 리뷰 불러오기
async function queryReviewData(databaseId: string, seminarId: string): Promise<any[]> {
try {
const response = await notion.databases.query({
database_id: databaseId,
filter: {
property: 'Seminar',
relation: {
contains: seminarId
}
}
});

return response.results;
} catch (error) {
console.error('Error querying Notion database by Seminar ID:', error);
throw error;
}
}

export async function GET(req: NextRequest) {
const url = new URL(req.url);
const seminarId = url.searchParams.get('seminarId'); // 쿼리 파라미터에서 세미나 ID 가져오기

if (!seminarId) {
return new Response(JSON.stringify({ message: 'Seminar ID is required' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
},
});
}

const databaseId = process.env.NOTION_REVIEW_DATABASE_ID || ''; // 리뷰 데이터베이스 ID

try {
const reviews = await queryReviewData(databaseId, seminarId);
return new Response(JSON.stringify({ reviews, message: 'Success' }), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
return new Response(JSON.stringify({ message: `Failed: ${error?.toString()}` }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}
84 changes: 38 additions & 46 deletions src/app/seminar/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,56 @@
'use client';

import React, {useEffect} from 'react';
import { usePathname } from 'next/navigation';
import SeminarDetailHeader from '@/components/seminar/seminarDetail/header/SeminarDetailHeader';
import SeminarDetailBanner from '@/components/seminar/seminarDetail/banner/SeminarDetailBanner';
import { SEMINAR_DATA } from '@/constants/seminar/seminarData';
import { OPEN_SEMINAR_DATA } from '@/constants/seminar/openSeminarData';
import SeminarDetailPdf from '@/components/seminar/seminarDetail/pdf/SeminarDetailPdf';
import NotFoundPage from '@/app/not-found';
import SeminarDetailReview from '@/components/seminar/seminarDetail/review/SeminarDetailReview';
import { SeminarThumbnail } from '@/interfaces/seminar/seminarThumbnail';
import { changePathtoNumber } from '@/hooks/seminar/changePathtoNumber';

const SeminarDetailPage = () => {
const pathname = usePathname();
let id = changePathtoNumber(pathname || '');

let data: SeminarThumbnail | undefined;

// 일반객체
data = SEMINAR_DATA.find(seminar => `${seminar.id}` === `${id}`);

import { refactorSeminarData, refactorSeminarMemberData, refactorSeminarReviewData } from '@/hooks/seminar/notionDataRefactor';
import { headers } from "next/headers";
import { changePathtoSeperate } from '@/hooks/seminar/changePathtoNumber';
import { SEMINAR_MEMBER_DATA } from '@/constants/seminar/seminarMemberData';

const SeminarDetailPage = async () => {
// server comp에서 path 가져오기
const header = headers();
const pathname = header.get('next-url')

const seminarId = changePathtoSeperate(pathname ?? '');

// seminar data 정의
const seminarResponse = await fetch('http://localhost:3001/api/seminar');
const seminarList = await seminarResponse.json();
const seminars = refactorSeminarData(seminarList.data ?? []);

// reviews 데이터 정의
const seminarReviewResponse = await fetch(`http://localhost:3001/api/seminar/review?seminarId=${seminarId}`);
const seminarReviewList = await seminarReviewResponse.json();
const reviews = refactorSeminarReviewData(seminarReviewList.reviews ?? []);

// member 데이터 정의
const memberResponse = await fetch(`http://localhost:3001/api/member?seminarId=${seminarId}`);
const memberList = await memberResponse.json();
const member = refactorSeminarMemberData(memberList.data[0] ?? SEMINAR_MEMBER_DATA);

// 세미나 디테일 데이터로 분리
const seminar = seminars.find(seminar => `${seminar.id}` === `${seminarId}`);
// 오픈 세미나 데이터
if (!data) {
let found = OPEN_SEMINAR_DATA.find(oseminar =>
oseminar.seminars.some(seminar => `0${oseminar.id}${seminar.id}` === `${id}`)
);

if (found) {
const matchingSeminar = found.seminars.find(seminar => `0${found.id}${seminar.id}` === `${id}`);
console.log('match', matchingSeminar);

if (matchingSeminar) {
data = matchingSeminar;
} else {
// 일치하는 세미나를 찾지 못한 경우
return <NotFoundPage />;
}
} else {
// 일치하는 OpenSeminar를 찾지 못한 경우
if (!seminar) {
return <NotFoundPage />;
}
}


return <section className="flex justify-center">
<div className="max-w-[1200px] desktop:px-10 tablet:px-10 px-4 bg-mono_black">
<div className="w-full">
{/* header */}
<SeminarDetailHeader key={`${data.id}_header`} data={data}/>

{/* banner */}
<SeminarDetailBanner key={`${data.id}_banner`} data={data}/>
{/* header */}
<SeminarDetailHeader key={`${seminar.id}_header`} seminar={seminar}/>

{/* pdf file */}
<SeminarDetailPdf key={`${data.id}_pdf`}/>
{/* banner */}
<SeminarDetailBanner key={`${seminar.id}_banner`} seminar={seminar} member={member}/>

{/* review */}
<SeminarDetailReview key={`${data.id}_review`} data={data.reviews} />
{/* pdf file */}
<SeminarDetailPdf key={`${seminar.id}_pdf`} seminar={seminar}/>

{/* review */}
<SeminarDetailReview key={`${seminar.id}_review`} reviews={reviews} />
</div>
<div className="h-[7.5rem]"></div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
'use client';

import React from 'react';
import Image from 'next/image';
import { SeminarThumbnail } from '@/interfaces/seminar/seminarThumbnail';
import TranslateImg from '@/svg/seminar/translate_img.png';
import { motion } from 'framer-motion';
import { seminarCardVariants } from '@/constants/seminar/seminarCardVariants';
import SeminarDetailBannerInform from './SeminarDetailBannerInform';
import { SeminarMember } from '@/interfaces/seminar/seminarMember';


/**
Expand All @@ -18,7 +21,7 @@ import SeminarDetailBannerInform from './SeminarDetailBannerInform';
* Renders the header component for the recruitment section.
* @returns The rendered header component.
*/
const SeminarDetailBanner = ({ data }: { data: SeminarThumbnail }) => {
const SeminarDetailBanner = ({ seminar, member }: { seminar: SeminarThumbnail, member: SeminarMember }) => {
return (
<div className="w-full mt-8 pb-6 flex desktop:flex-row tablet:flex-col flex-col flex-col gap-8">
{/* 왼쪽 컨텐츠 */}
Expand All @@ -31,14 +34,15 @@ const SeminarDetailBanner = ({ data }: { data: SeminarThumbnail }) => {
variants={seminarCardVariants}
style={{ transformOrigin: '10% 60%' }}
>
<div className='rounded-lg overflow-hidden'>
<Image
src={data.seminar_image_url}
alt={`${data.presenter_name}'s_seminarimage`}
src={seminar.seminar_image_url}
alt={`${seminar.title} image`}
width={1600}
height={900}
quality={100}
priority
/>
</div>
</motion.section>
</div>
{/* 오른쪽 컨텐츠 */}
Expand All @@ -51,12 +55,11 @@ const SeminarDetailBanner = ({ data }: { data: SeminarThumbnail }) => {
width={352}
height={56}
quality={100}
priority
/>
</div>
<SeminarDetailBannerInform type='Date' data={data.date} speaker_data=''/>
<SeminarDetailBannerInform type='Location' data={data.location} speaker_data=''/>
<SeminarDetailBannerInform type='Speaker' data={data.presenter_name} speaker_data={data.presenter_role}/>
<SeminarDetailBannerInform type='Date' data={seminar.date} speaker_data=''/>
<SeminarDetailBannerInform type='Location' data={seminar.location} speaker_data=''/>
<SeminarDetailBannerInform type='Speaker' data={member.name} speaker_data={member.role}/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import BannerImg from '@/svg/seminar/seminar_banner.svg';
* Renders the header component for the recruitment section.
* @returns The rendered header component.
*/
const SeminarDetailHeader = ({ data }: { data: SeminarThumbnail }) => {
const [categoryData, setCategoryData] = useState([data.type, `${data.flag}st`, data.topic]);
const SeminarDetailHeader = ({ seminar }: { seminar: SeminarThumbnail }) => {
const [categoryData, setCategoryData] = useState([seminar.type, `${seminar.flag}st`, seminar.topic]);

return (
<>
Expand All @@ -31,10 +31,10 @@ const SeminarDetailHeader = ({ data }: { data: SeminarThumbnail }) => {
))}
</div>
<p className="w-full px-3 pt-8 H3">
{data.title}
{seminar.title}
</p>
<p className="w-full px-3 pt-3 H6">
{data.description}
{seminar.description}
</p>
</>
);
Expand Down
9 changes: 7 additions & 2 deletions src/components/seminar/seminarDetail/pdf/SeminarDetailPdf.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
'use client';

import React from 'react';
import PinImg from '@/svg/seminar/pin.svg';
import { motion } from 'framer-motion';
import { seminarCardVariants } from '@/constants/seminar/seminarCardVariants';
import { SeminarThumbnail } from '@/interfaces/seminar/seminarThumbnail';

/**
* @description
Expand All @@ -14,7 +17,7 @@ import { seminarCardVariants } from '@/constants/seminar/seminarCardVariants';
* Renders the header component for the recruitment section.
* @returns The rendered header component.
*/
const SeminarDetailPdf = () => {
const SeminarDetailPdf = ({seminar}:{seminar:SeminarThumbnail}) => {

return (
<motion.section
Expand All @@ -35,7 +38,9 @@ const SeminarDetailPdf = () => {
className="flex gap-2 cursor-pointer"
>
<PinImg />
<p className='B1'>세미나 자료 보기</p>
<a href={seminar.pdf_url} download={`${seminar.title}_${seminar.date}`} className="B1">
세미나 자료 보기
</a>
</motion.section>
</motion.section>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ import { seminarCardVariants } from '@/constants/seminar/seminarCardVariants';
* Renders the header component for the recruitment section.
* @returns The rendered header component.
*/
const SeminarDetailReviewDetail = ({ data }: { data: SeminarReview }) => {
const SeminarDetailReviewDetail = ({ review }: { review: SeminarReview }) => {

return (
<div className="mt-5 flex flex-col p-5 bg-mono_900 rounded-xl">
<p className='B1 pointer-events-none'>{data.author}</p>
<p className='B1 pointer-events-none'>{review.author}</p>
<motion.section
viewport={{ once: true, amount: 0.9 }}
variants={seminarCardVariants}
style={{ transformOrigin: '10% 60%' }}
whileHover={{ scale: 1.05, transition: { duration: 0.2 } }}
whileTap={{ scale: 0.8 }}
>
<p className='mt-3 B1 pointer-events-none'>{data.content}</p>
<p className='mt-3 B1 pointer-events-none'>{review.content}</p>
</motion.section>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from 'react';
'use client';

import React, {useEffect, useState} from 'react';
import SeminarDetailReviewDetail from './SeminalDetailReviewDetail';
import { motion } from 'framer-motion';
import { seminarCardVariants } from '@/constants/seminar/seminarCardVariants';
Expand All @@ -15,15 +17,15 @@ import { SeminarReview } from '@/interfaces/seminar/seminarReview';
* Renders the header component for the recruitment section.
* @returns The rendered header component.
*/
const SeminarDetailReview = ({ data }: { data: SeminarReview[] }) => {
const SeminarDetailReview = ({reviews}:{reviews:SeminarReview[]}) => {

return (
<div className="w-full flex mt-10 flex-col inline-flex min-h-fit relative">
<p className="H4 px-2 py-3 pointer-events-none">
Review
</p>
<p className="mb-3 border border-solid text-mono_700 h-0"/>
{data.map((review) => (
{reviews.map((review) => (
<motion.section
key={review.id}
initial={{ y: 20, opacity: 0 }}
Expand All @@ -35,7 +37,7 @@ const SeminarDetailReview = ({ data }: { data: SeminarReview[] }) => {
>
<SeminarDetailReviewDetail
key={`${review.id}_review`}
data={review}/>
review={review}/>
</motion.section>
))}
</div>
Expand Down
Loading

0 comments on commit 84783ec

Please sign in to comment.