Skip to content

Commit

Permalink
Merge pull request #77 from SlamTalk/feature/54-court-info-update
Browse files Browse the repository at this point in the history
#54 style: 농구장 자세히 보기 UI 수정
  • Loading branch information
hi-rachel committed Feb 2, 2024
2 parents 2f55399 + 6de2c36 commit 48287f7
Show file tree
Hide file tree
Showing 11 changed files with 497 additions and 225 deletions.
Binary file added public/images/han-river-park-court.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/app/(auth)/user-info/page.tsx
Expand Up @@ -31,7 +31,7 @@ const SignUpProcess = () => {
} else {
try {
// await axiosInstance.patch('/api/user/update/info', userInfo);
router.push('/login');
router.push('/');
} catch (error) {
console.error('정보 전송 실패:', error);
}
Expand Down
309 changes: 166 additions & 143 deletions src/app/map/components/CourtDetail.tsx
@@ -1,192 +1,215 @@
'use client';

import React, { useEffect, useState } from 'react';
import { Textarea, Input, RadioGroup, Radio, Button } from '@nextui-org/react';
import React from 'react';
import { Button } from '@nextui-org/react';
import { IoIosClose } from 'react-icons/io';
import { FaPhone } from 'react-icons/fa';
import { PiChatsCircle } from 'react-icons/pi';
import { FaPhoneAlt, FaParking, FaTag, FaRegDotCircle } from 'react-icons/fa';
import Image from 'next/image';
import { FaLocationDot, FaClock, FaLightbulb } from 'react-icons/fa6';
import { PiChatsCircle } from 'react-icons/pi';
import { CourtIcon } from './icons/CourtIcon';
import { HoopIcon } from './icons/HoopIcon';
import { FeeIcon } from './icons/FeeIcon';
import { InfoIcon } from './icons/InfoIcon';
import { WebsiteIcon } from './icons/WebsiteIcon';

interface CourtDetailsProps {
isVisible: boolean;
onClose: () => void;
selectedPlace: any;
}

// 농구장 사진, 주소(도로명), 코트 종류, 실내외(실내/야외), 코트사이즈, 골대수, 야간 조명, 개방시간, 사용료, 주차 가능, 전화번호, 홈페이지, 기타 정보
// [TODO] 주소 복사 넣기, ✅
// 각 컨텐츠 아이콘 넣기 ✅
// true, false 명확히하기 ✅
// 현재 링크 공유 만들기

const CourtDetails: React.FC<CourtDetailsProps> = ({
isVisible,
onClose,
selectedPlace,
}) => {
const [otherInfo, setOtherInfo] = useState<string>(
'주차하기 어려워요. 대중교통 이용 추천해요.'
);
const [isInfoModified, setIsInfoModified] = useState<boolean>(false);

// api 개발 완료 후 주석 처리 부분 연결 예정
useEffect(() => {
// otherInfo 상태가 변경될 때마다 isInfoModified 상태를 설정
if (otherInfo !== selectedPlace.otherInfo) {
setIsInfoModified(true);
} else {
setIsInfoModified(false);
}
}, [otherInfo, selectedPlace]);

const handleSaveClick = async () => {
// try {
// const response = await axios.post('/api/map/marker/{court_id}/info/save', {
// placeId: selectedPlace.id,
// otherInfo,
// });
// console.log('데이터 저장 성공:', response.data);
// setIsInfoModified(false); // 저장 후 isInfoModified를 false로 설정하여 저장 버튼 숨김
// } catch (error) {
// console.error('데이터 저장 실패:', error);
// }
};

const handleOtherInfoChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setOtherInfo(e.target.value);
};

const handlePhoneClick = () => {
if (selectedPlace.phone) {
if (selectedPlace.phoneNum) {
const confirmDial = window.confirm(
`이 전화번호로 연결하시겠습니까? ${selectedPlace.phone}`
`이 전화번호로 연결하시겠습니까? ${selectedPlace.phoneNum}`
);
if (confirmDial) {
window.location.href = `tel:${selectedPlace.phone}`;
window.location.href = `tel:${selectedPlace.phoneNum}`;
}
}
};

const handleCopyAddress = async () => {
if (selectedPlace.address) {
try {
await navigator.clipboard.writeText(selectedPlace.address);
alert('주소가 복사되었습니다.');
} catch (error) {
console.error('주소 복사 중 오류 발생:', error);
alert('주소를 복사하는 데 실패했습니다.');
}
}
};

return (
<div
className={`absolute inset-0 z-40 m-auto h-fit w-full max-w-md rounded-lg border-1 bg-background
shadow-md transition-all duration-300 ease-in-out ${isVisible ? '' : 'hidden'}`}
className={`absolute inset-0 z-40 m-auto h-fit w-full max-w-md rounded-lg bg-background shadow-md
transition-all duration-300 ease-in-out ${isVisible ? '' : 'hidden'}`}
>
<div className="relative max-h-[85vh] w-full overflow-auto">
<div className="relative h-28 w-full">
<div className="relative max-h-[96vh] w-full overflow-auto rounded-lg text-sm">
<div className="relative h-48 w-full">
<Image
layout="fill"
objectFit="contain"
alt="농구장 사진"
src="/images/court.svg"
src="/images/han-river-park-court.png"
/>
{isInfoModified && (
<Button
size="sm"
className="bg-gradient absolute right-9 top-3"
onClick={handleSaveClick}
aria-label="Save"
>
저장
</Button>
)}
<Button
isIconOnly
className="bg-gradient absolute right-2 top-2"
onClick={onClose}
aria-label="Close"
>
<IoIosClose size={22} />
<IoIosClose size={30} className="text-gray-600" />
</Button>
</div>
<div className="mb-5 px-8">
<div className="my-4 flex justify-between">
<div>
<h2 className="text-lg font-semibold">
{selectedPlace.place_name}
</h2>
<p>{selectedPlace.address_name}</p>
<div className="mb-5 px-8 py-4">
<div className="flex justify-between">
<h2 className="mx-1 text-xl font-bold">
{selectedPlace.courtName}
</h2>
<Button
radius="full"
size="sm"
variant="bordered"
startContent={<PiChatsCircle />}
className="border-1"
aria-label="시설 채팅 바로가기"
>
시설 채팅
</Button>
</div>
<div className="my-4 flex w-full justify-center gap-3">
<Button
color="primary"
variant="bordered"
startContent={<FaRegDotCircle />}
radius="full"
className="border-1"
>
출발
</Button>
<Button
color="primary"
radius="full"
startContent={<FaLocationDot />}
>
도착
</Button>
</div>
<hr className="w-90 my-4 h-px bg-gray-300" />
<div className="my-4 flex flex-col gap-4">
<div className="flex gap-2 align-middle">
<FaLocationDot
size={16}
className="dark:text-gray-20 text-gray-400"
/>
<span>{selectedPlace.address}</span>
<button type="button" onClick={handleCopyAddress}>
<span className="text-blue-500">복사</span>
</button>
</div>
<div className="flex gap-3">
{selectedPlace.phone && (
<FaPhone
size={20}
onClick={handlePhoneClick}
style={{ cursor: 'pointer' }}
/>
)}
<PiChatsCircle size={24} />
<div className="flex gap-2 align-middle">
<FaClock size={14} className="text-gray-400 dark:text-gray-200" />
<span>
개방 시간:{' '}
<span className="text-rose-400">
{selectedPlace.openingHours === true ? '24시간' : '제한'}
</span>
</span>
</div>
<div className="flex gap-2 align-middle">
<FaPhoneAlt
size={15}
className="pointer-events-auto text-gray-400 dark:text-gray-200"
onClick={handlePhoneClick}
/>
<span>
{selectedPlace.phoneNum ? selectedPlace.phoneNum : '-'}
</span>
</div>
<div className="flex gap-2 align-middle">
<FeeIcon className="text-gray-400 dark:text-gray-200" />
<span className="text-info text-blue-500">
이용료: {selectedPlace.fee === true ? '유료' : '무료'}
</span>
</div>
</div>

<div className="flex flex-col gap-4">
<div className="flex gap-4 md:flex-nowrap">
<Input
type="text"
isReadOnly
labelPlacement="outside"
label="코트 종류"
defaultValue="우레탄"
variant="bordered"
className="text-b max-w-xs"
<div className="flex gap-2 align-middle">
<WebsiteIcon className="text-gray-400 dark:text-gray-200" />
<span className="text-blue-500">
{selectedPlace.website !== null ? selectedPlace.website : '-'}
</span>
</div>
<div className="flex gap-2 align-middle">
<CourtIcon className="text-gray-400 dark:text-gray-200" />
<span className="font-medium">
코트 종류: {selectedPlace.courtType}
</span>
</div>
<div className="flex gap-2 align-middle">
<CourtIcon className="text-gray-400 dark:text-gray-200" />
<span className="font-medium">
코트 사이즈: {selectedPlace.courtSize}
</span>
</div>
<div className="flex gap-2 align-middle">
<HoopIcon className="text-gray-400 dark:text-gray-200" />
<span className="font-medium">
골대 수: {selectedPlace.hoopCount}
</span>
</div>
<div className="flex gap-2 align-middle">
<FaLightbulb
size={17}
className="text-gray-400 dark:text-gray-200"
/>
<Input
type="text"
isReadOnly
labelPlacement="outside"
label="코트 사이즈"
defaultValue="풀코트"
variant="bordered"
className="max-w-xs"
<span>
야간 조명:{' '}
{selectedPlace.nightLighting === true ? '있음' : '없음'}
</span>
</div>
<div className="flex gap-2 align-middle">
<FaParking
size={17}
className="text-gray-400 dark:text-gray-200"
/>
<span>
주차: {selectedPlace.parkingAvailabl === true ? '가능' : '불가'}
</span>
</div>

<Input
isReadOnly
labelPlacement="outside"
variant="bordered"
type="number"
label="골대 수"
defaultValue="4"
/>
<RadioGroup
isReadOnly
defaultValue="있음"
label="야간 조명"
orientation="horizontal"
>
<Radio value="있음">있음</Radio>
<Radio value="없음">없음</Radio>
</RadioGroup>
<RadioGroup
isReadOnly
defaultValue="24시"
label="개방 시간"
orientation="horizontal"
>
<Radio value="제한">제한</Radio>
<Radio value="24시">24시</Radio>
</RadioGroup>
<RadioGroup
isReadOnly
defaultValue="무료"
label="사용료"
orientation="horizontal"
>
<Radio value="무료">무료</Radio>
<Radio value="유료">유료</Radio>
</RadioGroup>
<RadioGroup
isReadOnly
defaultValue="불가능"
label="주차여부"
orientation="horizontal"
>
<Radio value="가능">가능</Radio>
<Radio value="불가능">불가능</Radio>
</RadioGroup>
<div className="w-full">
<Textarea
variant="bordered"
label="기타 정보"
defaultValue={otherInfo}
value={otherInfo}
onChange={handleOtherInfoChange}
/>
<div className="flex gap-2 align-middle text-sm">
<FaTag size={17} className="text-gray-400 dark:text-gray-200" />
<span className="rounded-sm bg-gray-100 px-1 text-gray-500 dark:bg-gray-300 dark:text-gray-600">
{selectedPlace.indoorOutdoor}
</span>
<ul className="flex gap-2">
{selectedPlace.convenience.map((tag: string, idx: number) => (
<li
// eslint-disable-next-line react/no-array-index-key
key={idx}
className="rounded-sm bg-gray-100 px-1 text-gray-500 dark:bg-gray-300 dark:text-gray-600"
>
<span>{tag}</span>
</li>
))}
</ul>
</div>
<div className="flex gap-2 align-middle">
<InfoIcon className="text-gray-400 dark:text-gray-200" />
<span className="text-sm">{selectedPlace.additionalInfo}</span>
</div>
</div>
</div>
Expand Down

0 comments on commit 48287f7

Please sign in to comment.