Skip to content

Commit

Permalink
Feat: #138 map write (#139)
Browse files Browse the repository at this point in the history
* feat: 별점 반영

* feat: 리뷰, 글쓰기 페이지

* feat: 글쓰기 페이지

* feat: 리뷰페이지

---------

Co-authored-by: Hyun <95120267+LIM-HYUN-JEONG@users.noreply.github.com>
  • Loading branch information
IM-HYUN-JEONG and IM-HYUN-JEONG authored May 16, 2024
1 parent e64a4bc commit 3fe10e6
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 28 deletions.
19 changes: 5 additions & 14 deletions src/app/service/map/_components/bottom-sheet/BottomSheetHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Phone, Star, StarHalf, ChevronRight } from 'lucide-react';
import SquarePen from 'public/square-pen.svg';
import { BottomSheetInnerProps } from '@/types/map/BottomSheetProps';
import useReviewStore from '@/store/review/reviewStore';
import { Rating } from '@mui/material';

function BottomSheetHeader({ data }: BottomSheetInnerProps) {
const {
Expand Down Expand Up @@ -72,20 +73,10 @@ function BottomSheetHeader({ data }: BottomSheetInnerProps) {
</section>
{/* 5개 빈 별공간 */}
<section className="flex text-sm leading-5 font-semibold text-black">
<div className="relative">
<div className="flex gap-4px">
{Array.from({ length: 5 }, (_, index) => (
<Star key={index} fill="#C3C6CC" strokeWidth={0} />
))}
</div>
{/* 별 점수에 따라 넣어야함 (로직 아직안함), 별점 점수도 바꿔야함 */}
<div className="flex gap-4px absolute top-0">
<Star fill="#FFCB29" strokeWidth={0} />
<Star fill="#FFCB29" strokeWidth={0} />
<StarHalf fill="#FFCB29" strokeWidth={0} />
</div>
</div>
별점 점수
<Rating value={score_average} readOnly />
<span className="ml-1 font-semibold content-center">
{score_average}
</span>
<ChevronRight size={24} strokeWidth={1} />
</section>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @next/next/no-img-element */
/* eslint-disable function-paren-newline */

import { useToast } from '@/app/_components/ui/use-toast';
import { BottomSheetInnerProps } from '@/types/map/BottomSheetProps';
import { ReviewListProps } from '@/types/review/ReviewProps';
import { ReviewResponseType } from '@/types/review/review';
import { ChevronDown, ChevronUp, Copy } from 'lucide-react';
import Image from 'next/image';
import { useState } from 'react';
import { Map, MapMarker } from 'react-kakao-maps-sdk';

Expand All @@ -14,6 +16,56 @@ export default function BottomSheetReviewTab({
data: ReviewResponseType;
}) {
const { reviews, gym_name, cursor } = data; // 리뷰 데이터
console.log('reviews', reviews);

return <div>d</div>;
return (
<div>
<div>
{reviews.map(review => (
<div key={review.id} className="flex flex-col items-center">
{/* 이미지 */}
{/* ToDo : 이미지 어떻게 보여질지 라이브러리 찾아봐야함 */}
{review.images &&
review.images.map((image, index) => (
<Image
className="rounded-lg w-80 h-80"
key={index}
src={image}
alt="review_img"
width={320}
height={320}
/>
))}
{/* 리뷰 작성자 */}
<div className="flex">
<Image
className="rounded-full"
src={review.writer.profile_image}
alt="profile_img"
width={24}
height={24}
/>
<div>(*등급 정보없음..?)</div>
<p className="text-gray-900 font-semibold text-sm">
{review.writer.nickname}
</p>
<div className="text-gray-600 font-medium text-sm">
#(*태그 정보없음..?)
</div>
</div>
{/* 리뷰 내용 */}
<p>{review.content}</p>
{/* 리뷰에 대한 반응들 */}
<ul>
{review.review_reaction_count.map(reaction => (
<li key={reaction.type}>
{reaction.type}: {reaction.count}
</li>
))}
</ul>
</div>
))}
</div>
</div>
);
}
176 changes: 166 additions & 10 deletions src/app/service/map/_components/bottom-sheet/WriteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import ReviewRegisterModal from '@/app/_components/review/review-modal/ReviewReg
import OneReview from '@/app/_components/review/review-modal/OneReview';
import { Rating } from '@mui/material';
import { ReviewRegisterType } from '@/types/review/review';
import Image from 'next/image';
/**
* @description 지도 위에 띄위기 위해서 Modal로 구현을 합니다.
* @param position 어느 방향에서 모달이 열릴지 결정합니다.
Expand Down Expand Up @@ -66,6 +67,56 @@ function WriteModal({ openPosition }: ReviewProps) {
if (isLoadingMore || isEmpty) {
return <ReviewModalSkeleton openPosition={openPosition} />;
}
const [inputData, setInputData] = useState<{ value: string; count: number }>({
value: '',
count: 0,
});
const [textAreaData, setTextAreaData] = useState<{
value: string;
count: number;
}>({ value: '', count: 0 });

const onInputHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setInputData({
value,
count: value.replace(/[\0-\x7f]|([0-\u07ff]|(.))/g, '$&$1$2').length,
});
};

const onTextAreaHandler = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value;
setTextAreaData({
value,
count: value.replace(/[\0-\x7f]|([0-\u07ff]|(.))/g, '$&$1$2').length,
});
};
const [postImg, setPostImg] = useState<File[]>([]); // 서버 저장하는 파일 상태
const [previewImg, setPreviewImg] = useState<string[]>([]); // 미리보기 이미지를 생성하기 위한 상태 (원본이미지용량이 크기에 따로 분리)

const uploadFile = (e: React.ChangeEvent<HTMLInputElement>) => {
const fileArr = e.target.files;
if (fileArr) {
const files = Array.from(fileArr);
setPostImg(files);

const newPreviewImg: string[] = [];

// 한 번만 생성하고 재사용
const fileReader = new FileReader();
fileReader.onload = () => {
const result = fileReader.result as string;
setPreviewImg([...previewImg, result]);
};

// 파일마다 미리보기 이미지 생성
files.forEach(file => {
fileReader.readAsDataURL(file);
});
}
};

const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {};

return (
<>
Expand All @@ -74,19 +125,94 @@ function WriteModal({ openPosition }: ReviewProps) {
gym_name={reviews[0].data.data.gym_name}
/>
<div className={modalClassName}>
{/* Top */}
<div className="w-full bg-white max-w-[768px] z-[101] h-[3.5rem] fixed shadow flex items-center justify-center">
<button type="button" className={positionClassName} onClick={reset}>
{openPosition === 'center' ? <ChevronDown /> : <ChevronLeft />}
</button>
{reviews[0].data.data.gym_name} 글쓰기
<div className="font-semibold text-lg">글쓰기</div>
</div>
{/* Middle */}
<div className="relative mt-[3.5rem]">
{/* 리뷰남기기 */}
<div className="text-center">
<div className="text-center">
<div className="">이곳에 대해 총 평점은?</div>
<form
name="profile"
action="/action_page.php"
method="get"
className="p-5"
>
{/* Middle */}
<div className="relative mt-[3.5rem]">
{/* 운동장소 */}
<div className="mb-6">
<div className="font-bold text-base">
운동장소<span className="font-bold text-[#FF5247] "> *</span>
</div>
<div className="w-full rounded-md h-10 bg-[#F4F5F7] text-[#A5A7AE] flex items-center justify-center border border-gray-300">
{reviews[0].data.data.gym_name}
</div>
</div>
{/* 해당 암장 본인레벨 */}
<div className="mb-6">
<div className="font-bold text-base">
<div>
해당 암장 본인레벨
<span className="font-bold text-[#FF5247] "> *</span>
</div>
<div className="w-full rounded-md h-10 bg-[#F4F5F7] text-[#A5A7AE] flex items-center justify-center border border-gray-300">
Click
</div>
</div>
</div>

{/* 한줄 기록 */}
<div className="mb-6">
<div>
<div className="flex justify-between">
<div className="font-bold text-base">
한줄 기록
<span className="font-bold text-[#FF5247] "> *</span>
</div>
<p className="font-normal text-sm text-gray-600">
<span>{inputData.count}</span>
<span>/30</span>
</p>
</div>
<input
className="w-full border-b"
required
type="text"
name="alias"
onChange={onInputHandler}
maxLength={30}
placeholder="운동시설은 어땠나요?"
/>
</div>
</div>

{/* 상세 내용 (선택) */}
<div className="mb-6">
<div>
<div className="flex justify-between">
<div className="font-bold text-base">상세 내용 (선택)</div>
<p className="font-normal text-sm text-gray-500">
<span>{textAreaData.count}</span>
<span>/500</span>
</p>
</div>
<textarea
className="w-full border-b"
name="opinion"
onChange={onTextAreaHandler}
maxLength={500}
placeholder="다른 유저에게 도움이 될 만한 정보를 입력해주세요."
/>
</div>
</div>
</div>

{/* Bottom - 사진 및 제출 버튼 */}
<div className="text-center mb-6">
<div className="mb-6">
<div className="font-semibold text-lg">
이곳에 대해 총 평점은?
</div>
<Rating
name="rating"
size="large"
Expand All @@ -107,9 +233,39 @@ function WriteModal({ openPosition }: ReviewProps) {
}}
/>
</div>
<button type="button">작성완료</button>
<div className="border-3 flex">
<input
accept=".png, .jpg, .jpeg"
type="file"
onChange={uploadFile}
style={{
width: '100px',
height: '100px',
color: '#F0F1F3',
border: '1px solid #D1D1D1',
borderRadius: '5px',
}}
/>
{previewImg &&
previewImg.map((imgSrc, i) => (
<div key={i} className="w-24 h-24">
<Image
alt={`Uploaded Image ${i}`}
src={imgSrc}
width={100}
height={100}
/>
</div>
))}
</div>
<button
type="submit"
className="w-full rounded-md h-10 bg-gray-200 text-gray-600 mt-4"
>
작성완료
</button>
</div>
</div>
</form>
</div>
</>
);
Expand Down
6 changes: 3 additions & 3 deletions src/types/review/review.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
export interface ReactionType {
type: 'interest' | 'like' | 'help' | 'thumb' | 'angry';
type: 'wantToGo' | 'helped' | 'great' | 'funny';
count: number;
}

export interface ReviewDataType {
id: number;
is_mine: boolean;
my_reaction: 'interest' | 'like' | 'help' | 'thumb' | 'angry' | null;
my_reaction: 'wantToGo' | 'helped' | 'great' | 'funny' | null;
review_reaction_count: ReactionType[];
content: string;
images: string[];
Expand All @@ -16,7 +16,7 @@ export interface ReviewDataType {
writer: {
id: number;
nickname: string;
profileImage: string;
profile_image: string;
};
}

Expand Down

0 comments on commit 3fe10e6

Please sign in to comment.