Skip to content

Commit

Permalink
Merge pull request #229 from TripInfoWeb/dev_informations
Browse files Browse the repository at this point in the history
Feat: 정보 북마크 API 연동 및 페이지네이션 API 재연동
  • Loading branch information
HyunJinNo authored Aug 26, 2024
2 parents a18c32f + dc21bf2 commit 8bd35de
Show file tree
Hide file tree
Showing 38 changed files with 478 additions and 172 deletions.
3 changes: 3 additions & 0 deletions public/bookmark-icon-marked.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions src/app/api/bookmark/information/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { revalidatePath, revalidateTag } from "next/cache";
import { NextRequest } from "next/server";

/**
* 정보 북마크 등록
*/
export async function POST(request: NextRequest) {
const cookie = request.cookies.get("access_token");
const body = await request.formData();

const data = new URLSearchParams();
data.append("infoId", body.get("infoId")?.toString() ?? "0");

const response = await fetch(
`${process.env.BACKEND_URL}/api/bookmark/information`,
{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Cookie: `${cookie?.name}=${cookie?.value}`,
},
body: data.toString(),
cache: "no-store",
},
);

revalidateTag("getBestInformationList");
revalidatePath("/informations", "layout");
return response;
}

/**
* 정보 북마크 취소
*/
export async function DELETE(request: NextRequest) {
const cookie = request.cookies.get("access_token");
const body = await request.formData();

const data = new URLSearchParams();
data.append("infoId", body.get("info")?.toString() ?? "0");

const response = await fetch(
`${process.env.BACKEND_URL}/api/bookmark/information`,
{
method: "DELETE",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Cookie: `${cookie?.name}=${cookie?.value}`,
},
body: data.toString(),
cache: "no-store",
},
);

revalidateTag("getBestInformationList");
revalidatePath("/informations", "layout");
return response;
}
8 changes: 0 additions & 8 deletions src/app/api/informations/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ export async function GET(
},
);

if (!response.ok) {
throw new Error(response.statusText);
}

return response;
} catch (e: any) {
return new Response(JSON.stringify({ error: e.message }), {
Expand Down Expand Up @@ -67,10 +63,6 @@ export async function PUT(
},
);

if (!response.ok) {
throw new Error(response.statusText);
}

// Revalidate the cache
revalidatePath("/informations", "layout");
return response;
Expand Down
58 changes: 58 additions & 0 deletions src/app/api/informations/great/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { revalidatePath, revalidateTag } from "next/cache";
import { NextRequest } from "next/server";

/**
* 정보 좋아요 등록
*/
export async function POST(request: NextRequest) {
const cookie = request.cookies.get("access_token");
const body = await request.formData();

const data = new URLSearchParams();
data.append("infoId", body.get("infoId")?.toString() ?? "0");

const response = await fetch(
`${process.env.BACKEND_URL}/api/information/great`,
{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Cookie: `${cookie?.name}=${cookie?.value}`,
},
body: data.toString(),
cache: "no-store",
},
);

revalidateTag("getBestInformationList");
revalidatePath("/informations", "layout");
return response;
}

/**
* 정보 좋아요 취소
*/
export async function DELETE(request: NextRequest) {
const cookie = request.cookies.get("access_token");
const body = await request.formData();

const data = new URLSearchParams();
data.append("infoId", body.get("infoId")?.toString() ?? "0");

const response = await fetch(
`${process.env.BACKEND_URL}/api/information/great`,
{
method: "DELETE",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Cookie: `${cookie?.name}=${cookie?.value}`,
},
body: data.toString(),
cache: "no-store",
},
);

revalidateTag("getBestInformationList");
revalidatePath("/informations", "layout");
return response;
}
2 changes: 1 addition & 1 deletion src/app/informations/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function getInformation(id: number) {

if (!response.ok) {
// This will activate the closest 'error.tsx' Error Boundary.
throw new Error("Failed to fetch data");
throw new Error(response.statusText);
}

return response.json() as Promise<InformationDetailDto>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,38 @@ import InformationListSkeleton from "@/components/skeleton/informations/list/Inf
import { Suspense } from "react";

interface Props {
params: { childCategoryId: string };
searchParams: { [key: string]: string | undefined };
}

export default function page({ params, searchParams }: Props) {
const categoryId = Number(params.childCategoryId);
if (categoryId <= 0 || !Number.isSafeInteger(categoryId)) {
throw new Error("Invalid CategoryId");
}

export default function page({ searchParams }: Props) {
const page = Number(searchParams["page"]);
if (page <= 0 || !Number.isSafeInteger(page)) {
throw new Error("Invalid Page Number");
}

const parentCategoryId = Number(searchParams["parentCategoryId"]);
if (parentCategoryId <= 0 || !Number.isSafeInteger(parentCategoryId)) {
throw new Error("Invalid ParentCategoryId");
}

const childCategoryId = Number(searchParams["childCategoryId"] || 0);
if (childCategoryId < 0 || !Number.isSafeInteger(childCategoryId)) {
throw new Error("Invalid ChildCategoryId");
}

return (
<div className="flex w-full flex-col items-center">
<Suspense fallback={<CategoryListSkeleton />}>
<CategoryList categoryId={categoryId} />
<CategoryList
parentCategoryId={parentCategoryId}
childCategoryId={childCategoryId}
/>
</Suspense>
<Suspense fallback={<InformationListSkeleton />}>
<InformationList
isParentCategory={false}
categoryId={categoryId}
page={page}
parentCategoryId={parentCategoryId}
childCategoryId={childCategoryId}
place={searchParams["place"]}
order={searchParams["order"]}
/>
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/common/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const Footer = () => {
<div className="flex flex-row items-center justify-between">
<Link
className="flex h-[2.625rem] w-[7.5rem] items-center justify-center rounded-3xl bg-black text-sm font-medium text-white hover:scale-105 dark:bg-slate-600"
href="/informations/list/parent-category/1?page=1"
href="/informations/list?page=1&parentCategoryId=1"
>
둘러보기
</Link>
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const Header = ({
: "font-medium text-gray1 dark:text-slate-400"
} ` + "text-sm hover:text-main"
}
href="/informations/list/parent-category/1?page=1"
href="/informations/list?page=1&parentCategoryId=1"
>
정보
</Link>
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/HeaderSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const HeaderSidebar = ({
</Link>
<Link
className="flex flex-row items-center gap-4 hover:text-main dark:text-slate-200"
href="/informations/list/parent-category/1?page=1"
href="/informations/list?page=1&parentCategoryId=1"
onClick={onClose}
onMouseEnter={() => setHoverNum(2)}
onMouseLeave={() => setHoverNum(0)}
Expand Down
41 changes: 28 additions & 13 deletions src/components/common/InformationItem.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import { CATEGORY_TEXT } from "@/constants/informations/category";
import Image from "next/image";
import Link from "next/link";
import { CiBookmark } from "react-icons/ci";
import { FaRegHeart } from "react-icons/fa";
import { TiLocation } from "react-icons/ti";
import HashSpinner from "./HashSpinner";

interface Props {
categoryId: number;
informationId: number;
categoryId: number;
userId?: number;
isBookMark: boolean;
title: string;
image: string;
address: string;
likeCount: number;
viewCount: number;
loading?: boolean;
onBookMarkClick: () => void;
}

const InformationItem = ({
categoryId,
informationId,
categoryId,
userId = 0,
isBookMark,
title,
image,
address,
likeCount,
viewCount,
loading = false,
onBookMarkClick,
}: Props) => {
let style = "";
switch (categoryId) {
Expand All @@ -41,6 +49,7 @@ const InformationItem = ({

return (
<div className="relative flex h-[19.6875rem] w-full flex-col justify-between rounded-2xl outline outline-1 outline-gray3 duration-300 hover:outline-main dark:outline-slate-400">
<HashSpinner loading={loading} />
<Image
className="-z-10 rounded-[0.875rem] dark:opacity-65"
src={image || "/next.svg"}
Expand All @@ -60,16 +69,22 @@ const InformationItem = ({
) : (
<div />
)}
<div className="relative h-7 w-5 cursor-pointer text-white hover:scale-110 dark:text-slate-200">
<Image
src="/bookmark-icon.svg"
alt="bookmark-icon"
fill={true}
style={{
objectFit: "contain",
}}
/>
</div>
{userId > 0 && (
<button
className="relative h-7 w-5 cursor-pointer text-white hover:scale-110 dark:text-slate-200"
type="button"
onClick={() => onBookMarkClick()}
>
<Image
src={`/bookmark-icon${isBookMark ? "-marked" : ""}.svg`}
alt="bookmark-icon"
fill={true}
style={{
objectFit: "contain",
}}
/>
</button>
)}
</div>
<div className="flex h-28 flex-col justify-between rounded-b-xl bg-white px-5 py-4 dark:bg-slate-800">
<Link
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/TopList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async function getTopInformationList() {

if (!response.ok) {
// This will activate the closest 'error.tsx' Error Boundary.
throw new Error("Failed to fetch data");
throw new Error(response.statusText);
}

return response.json() as Promise<TopInformationResponseDto[]>;
Expand All @@ -37,7 +37,7 @@ async function getTopGatheringList() {

if (!response.ok) {
// This will activate the closest 'error.tsx' Error Boundary.
throw new Error("Failed to fetch data");
throw new Error(response.statusText);
}

return response.json() as Promise<TopGatheringResponseDto[]>;
Expand Down
1 change: 0 additions & 1 deletion src/components/diary/list/DiaryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ interface Props {

const DiaryList = async ({ page }: Props) => {
const data = await getDiaryList(page - 1);
console.log(data);

return (
<div className="w-full">
Expand Down
5 changes: 3 additions & 2 deletions src/components/home/BestInformationList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BestInformationResponseDto } from "@/types/InformationDto";
import InformationItem from "../common/InformationItem";
import { CATEGORY_TEXT } from "@/constants/informations/category";
import { cookies } from "next/headers";
import InformationItemContainer from "@/containers/common/InformationItemContainer";

/**
* 좋아요 순으로 3개월 이내에 만들어진 정보 6개를 조회합니다.
Expand Down Expand Up @@ -33,10 +33,11 @@ const BestInformationList = async () => {
return (
<div className="mt-6 grid w-full grid-cols-3 items-center gap-4 p-1 max-[1024px]:grid-cols-2 max-[744px]:w-[120.6rem] max-[744px]:grid-cols-6 max-[744px]:grid-rows-1">
{data.map((value, index) => (
<InformationItem
<InformationItemContainer
key={index}
informationId={value.informationId}
categoryId={Number(CATEGORY_TEXT[value.parentCategoryName])}
isBookMark={value.isBookMark}
title={value.title}
image={value.thumbNailImage}
address={`${value.zoneCategoryParentName}, ${value.zoneCategoryChildName}`}
Expand Down
Loading

0 comments on commit 8bd35de

Please sign in to comment.