Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { useState } from 'react';
import Image from 'next/image';
import { toast } from 'react-toastify';
import Dropdown from '@/components/Dropdown';
import kebab from '@/assets/icons/menu.svg';
import Modal from '@/components/modal/Modal';
import DeleteWineForm from '@/components/modal/DeleteWineModal';
import PatchReviewForm from '@/components/modal/PatchReviewForm';
import PatchReviewModal from '@/components/modal/PatchReviewModal';
import DeleteModal from '@/components/modal/DeleteModal';
import { MyReview } from '@/types/review-data';
import { fetchDeleteReview } from '@/lib/fetchMyReivew';
import { EditReviewData } from '@/app/(with-header)/myprofile/_components/MyReviewListContainer';
import kebab from '@/assets/icons/menu.svg';

export default function MyReviewKebabDropDown({
reviewInitialData,
Expand Down Expand Up @@ -81,14 +81,16 @@ export default function MyReviewKebabDropDown({
<Modal
isOpen={isEditModalOpen}
setIsOpen={setIsEditModalOpen}
className={`overflow-x-hidden rounded-2xl mobile:mb-0 mobile:h-[930px] mobile:rounded-b-none ${
className={`min-w-[375px] rounded-2xl transition-transform mobile:fixed mobile:bottom-0 mobile:left-0 mobile:mb-0 mobile:w-full mobile:rounded-b-none ${
isEditModalOpen ? 'mobile:translate-y-0 mobile:animate-slide-up' : 'mobile:animate-slide-down mobile:translate-y-full'
}`}
>
<PatchReviewForm name={reviewInitialData.wine.name} id={id} onClose={closeEditModal} reviewInitialData={reviewInitialData} editMyReview={editMyReview} />
<div className='custom-scrollbar max-h-[90vh] overflow-y-auto'>
<PatchReviewModal name={reviewInitialData.wine.name} id={id} onClose={closeEditModal} reviewInitialData={reviewInitialData} editMyReview={editMyReview} />
</div>
</Modal>
<Modal isOpen={isDeleteModalOpen} setIsOpen={setIsDeleteModalOpen} className='rounded-2xl mobile:mx-auto mobile:h-[172px] mobile:max-w-[353px]'>
<DeleteWineForm onClose={closeDeleteModal} onDelete={handleDeleteWine} />
<DeleteModal onClose={closeDeleteModal} onDelete={handleDeleteWine} />
</Modal>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { useState } from 'react';
import Image from 'next/image';
import { toast } from 'react-toastify';
import Dropdown from '@/components/Dropdown';
import kebab from '@/assets/icons/menu.svg';
import Modal from '@/components/modal/Modal';
import PatchWineForm from '@/components/modal/PatchWineForm';
import DeleteWineForm from '@/components/modal/DeleteWineModal';
import PatchWineModal from '@/components/modal/PatchWineModal';
import DeleteModal from '@/components/modal/DeleteModal';
import { fetchDeleteWine } from '@/lib/fetchWines';
import kebab from '@/assets/icons/menu.svg';

export interface WineDataProps {
name: string;
Expand Down Expand Up @@ -89,10 +89,12 @@ export default function MyWIneKebabDropDown({
isEditModalOpen ? 'mobile:translate-y-0 mobile:animate-slide-up' : 'mobile:animate-slide-down mobile:translate-y-full'
}`}
>
<PatchWineForm onClose={closeEditModal} id={`${id}`} wineInitialData={wineInitialData} editMyWine={editMyWine} />
<div className='custom-scrollbar max-h-[90vh] overflow-y-auto'>
<PatchWineModal onClose={closeEditModal} id={`${id}`} wineInitialData={wineInitialData} editMyWine={editMyWine} />
</div>
</Modal>
<Modal isOpen={isDeleteModalOpen} setIsOpen={setIsDeleteModalOpen} className='rounded-2xl mobile:mx-auto mobile:h-[172px] mobile:max-w-[353px]'>
<DeleteWineForm onClose={closeDeleteModal} onDelete={handleDeleteWine} />
<DeleteModal onClose={closeDeleteModal} onDelete={handleDeleteWine} />
</Modal>
</div>
);
Expand Down
18 changes: 10 additions & 8 deletions src/app/(with-header)/wines/[id]/_components/ReviewDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
'use client';
import Image from 'next/image';

import { useState } from 'react';
import Image from 'next/image';
import { fetchWithAuth } from '@/lib/auth';
import { MyReview } from '@/types/review-data';
import { EditReviewData } from '@/types/review-data';
import { MyReview, EditReviewData } from '@/types/review-data';
import { toast } from 'react-toastify';
import Dropdown from '@/components/Dropdown';
import Modal from '@/components/modal/Modal';
import PatchReviewForm from '@/components/modal/PatchReviewForm';
import DeleteWineForm from '@/components/modal/DeleteWineModal';
import PatchReviewModal from '@/components/modal/PatchReviewModal';
import DeleteModal from '@/components/modal/DeleteModal';
import menu from '@/assets/icons/menu.svg';

export default function ReviewDropdown({
Expand Down Expand Up @@ -86,14 +86,16 @@ export default function ReviewDropdown({
<Modal
isOpen={isEditModalOpen}
setIsOpen={setIsEditModalOpen}
className={`overflow-x-hidden rounded-2xl mobile:mb-0 mobile:h-[930px] mobile:rounded-b-none ${
className={`min-w-[375px] rounded-2xl transition-transform mobile:fixed mobile:bottom-0 mobile:left-0 mobile:mb-0 mobile:w-full mobile:rounded-b-none ${
isEditModalOpen ? 'mobile:translate-y-0 mobile:animate-slide-up' : 'mobile:animate-slide-down mobile:translate-y-full'
}`}
>
<PatchReviewForm name={wineName} id={id} onClose={closeEditModal} reviewInitialData={reviewInitialData} editMyReview={editMyReview} />
<div className='custom-scrollbar max-h-[90vh] overflow-y-auto'>
<PatchReviewModal name={wineName} id={id} onClose={closeEditModal} reviewInitialData={reviewInitialData} editMyReview={editMyReview} />
</div>
</Modal>
<Modal isOpen={isDeleteModalOpen} setIsOpen={setIsDeleteModalOpen} className='rounded-2xl mobile:mx-auto mobile:h-[172px] mobile:w-[353px]'>
<DeleteWineForm onClose={closeDeleteModal} onDelete={handleDeleteWine} />
<DeleteModal onClose={closeDeleteModal} onDelete={handleDeleteWine} />
</Modal>
</div>
);
Expand Down
8 changes: 2 additions & 6 deletions src/app/(with-header)/wines/_components/WineFilterModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import FilterTypes from './FilterTypes';
import FilterPrice from './FilterPrice';
import FilterRating from './FilterRating';
import Button from '@/components/Button';
import { useScrollLock } from '@/hooks/useScrollLock';
import closeIcon from '@/assets/icons/close.svg';

const MAX_PRICE = 2000000;
Expand Down Expand Up @@ -46,12 +47,7 @@ export default function WineFilterModal({ isOpen, onClose, onApply, onFilterChan
onFilterChange({ type: selectedType, minPrice: priceRange[0], maxPrice: priceRange[1], rating: selectedRating });
}, [selectedType, priceRange, selectedRating, onFilterChange]);

useEffect(() => {
document.body.style.overflow = isOpen ? 'hidden' : 'auto';
return () => {
document.body.style.overflow = 'auto';
};
}, [isOpen]);
useScrollLock(isOpen);

const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
Expand Down
17 changes: 17 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ body {
min-width: 375px;
}

@layer base {
* {
@apply custom-scrollbar;
}
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: #CFDBEA;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background-color: #F2F4F8;
border-radius: 4px;
}
}

@layer utilities {
.line-clamp-2 {
display: -webkit-box;
Expand Down
4 changes: 2 additions & 2 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ProfileImgDropdown from './ProfileImgDropdown';

function LoggedOutHeader() {
return (
<div className='fixed top-[24px] z-50 flex w-full justify-center'>
<div className='fixed top-[24px] z-50 flex w-full justify-center' data-fixed='true'>
<div className='mx-auto h-[70px] w-[1140px] rounded-xl bg-black tablet:mx-6 tablet:max-w-[1000px] mobile:mx-5 mobile:h-[50px] mobile:min-w-[343px] mobile:max-w-[700px]'>
<header className='flex items-center justify-between px-[60px] py-[22.5px] mobile:px-[20px] mobile:py-[17.5px]'>
<Link href='/'>
Expand All @@ -28,7 +28,7 @@ function LoggedInHeader() {
const { profileImage } = useAuth();

return (
<div className='fixed top-[24px] z-50 flex w-full justify-center'>
<div className='fixed top-[24px] z-50 flex w-full justify-center' data-fixed='true'>
<div className='mx-auto h-[70px] w-[1140px] rounded-xl bg-black tablet:mx-6 tablet:max-w-[1000px] mobile:mx-5 mobile:h-[50px] mobile:min-w-[343px] mobile:max-w-[700px]'>
<header className='flex h-[70px] items-center justify-between px-[60px] py-[12.5px] mobile:h-[50px] mobile:px-[20px] mobile:py-[12.5px]'>
<Link href='/'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface DeleteWinePorps {
onDelete: () => void;
}

export default function DeleteWineForm({ onClose, onDelete }: DeleteWinePorps) {
export default function DeleteModal({ onClose, onDelete }: DeleteWinePorps) {
return (
<div>
<div className='flex h-[182px] w-[353px] flex-col justify-between px-4 pb-6 pt-8 mobile:mx-auto mobile:h-[172px]'>
Expand Down
3 changes: 3 additions & 0 deletions src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useScrollLock } from '@/hooks/useScrollLock';

interface ModalProps {
children: ReactNode;
Expand All @@ -14,6 +15,8 @@ export default function Modal({ children, isOpen, setIsOpen, className }: ModalP
const dialogRef = useRef<HTMLDialogElement>(null);
const [mounted, setMounted] = useState(false);

useScrollLock(isOpen);

const escKeyModalClose = useCallback(
(e: KeyboardEvent) => {
if (e.key === 'Escape') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ interface postReviewPorp {
editMyReview?: (id: number, editReviewData: EditReviewData, updatedAt: string) => void;
}

export default function PatchReviewForm({ name, id, onClose, reviewInitialData, editMyReview }: postReviewPorp) {
export default function PatchReviewModal({ name, id, onClose, reviewInitialData, editMyReview }: postReviewPorp) {
const [selectedAroma, setSelectedAroma] = useState<string[]>(reviewInitialData?.aroma || []);

const { register, handleSubmit, setValue, watch } = useForm<FormValues>({
Expand Down
126 changes: 74 additions & 52 deletions src/components/modal/PostReviewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,66 +213,88 @@ export default function PostReviewModal({ addReview }: { addReview: (newReview:
<Modal
isOpen={isOpen}
setIsOpen={setIsOpen}
className={`overflow-x-hidden rounded-2xl mobile:mb-0 mobile:h-[930px] mobile:rounded-b-none ${
className={`min-w-[375px] rounded-2xl transition-transform mobile:fixed mobile:bottom-0 mobile:left-0 mobile:mb-0 mobile:w-full mobile:rounded-b-none ${
isOpen ? 'mobile:translate-y-0 mobile:animate-slide-up' : 'mobile:animate-slide-down mobile:translate-y-full'
}`}
>
<div className='flex w-full flex-col gap-12 p-6 pc:w-[528px] tablet:w-[528px] mobile:h-[762px] mobile:w-full mobile:gap-10 mobile:py-8'>
<div className='flex items-center justify-between'>
<h1 className='text-2xl font-bold text-gray-800 mobile:text-xl'>리뷰 등록</h1>
<button type='button' onClick={closeModal}>
<Image src={close} width={34} height={34} className='mobile:h-[24px] mobile:w-[24px]' alt='창 닫기'></Image>
</button>
</div>
<form onSubmit={handleSubmit(handlePostReviewWine)}>
<div className='flex flex-col gap-10'>
<div className='flex flex-col gap-6'>
<div className='flex gap-4'>
<Image src={wineIcon} alt='와인 이미지' className='h-[68px] w-[68px] rounded-lg bg-gray-100 p-[7px] mobile:h-[67px] mobile:w-[67px]' />
<div className='flex flex-col gap-2'>
<p className='text-2lg font-semibold text-gray-800 mobile:text-lg'>{wineData.name}</p>
<InteractiveRating resetTrigger={resetTrigger} initialValue={0} size='large' onChange={(rate) => setValue('rating', rate)} />
<div className='custom-scrollbar max-h-[90vh] overflow-y-auto'>
<div className='flex w-full flex-col gap-12 p-6 pc:w-[528px] tablet:w-[528px] mobile:h-[762px] mobile:w-full mobile:gap-10 mobile:py-8'>
<div className='flex items-center justify-between'>
<h1 className='text-2xl font-bold text-gray-800 mobile:text-xl'>리뷰 등록</h1>
<button type='button' onClick={closeModal}>
<Image src={close} width={34} height={34} className='mobile:h-[24px] mobile:w-[24px]' alt='창 닫기'></Image>
</button>
</div>
<form onSubmit={handleSubmit(handlePostReviewWine)}>
<div className='flex flex-col gap-10'>
<div className='flex flex-col gap-6'>
<div className='flex gap-4'>
<Image src={wineIcon} alt='와인 이미지' className='h-[68px] w-[68px] rounded-lg bg-gray-100 p-[7px] mobile:h-[67px] mobile:w-[67px]' />
<div className='flex flex-col gap-2'>
<p className='text-2lg font-semibold text-gray-800 mobile:text-lg'>{wineData.name}</p>
<InteractiveRating resetTrigger={resetTrigger} initialValue={0} size='large' onChange={(rate) => setValue('rating', rate)} />
</div>
</div>
<textarea
placeholder='후기를 작성해 주세요'
className='h-[120px] resize-none rounded-2xl border border-gray-100 px-5 py-[14px] align-text-top placeholder:text-lg placeholder:font-normal placeholder:text-gray-500 focus:outline-purple-100 pc:h-[120px] pc:w-[480px] mobile:h-[100px] mobile:w-auto mobile:rounded-xl placeholder:mobile:text-md'
{...register('content')}
/>
</div>
<textarea
placeholder='후기를 작성해 주세요'
className='h-[120px] resize-none rounded-2xl border border-gray-100 px-5 py-[14px] align-text-top placeholder:text-lg placeholder:font-normal placeholder:text-gray-500 focus:outline-purple-100 pc:h-[120px] pc:w-[480px] mobile:h-[100px] mobile:w-auto mobile:rounded-xl placeholder:mobile:text-md'
{...register('content')}
/>
</div>
<div className='flex h-[212px] flex-col gap-6 mobile:h-[194px]'>
<h4 className='text-xl font-bold text-gray-800 mobile:text-2lg'>와인의 맛은 어땠나요?</h4>
<div>
<ControlBar label='바디감' minLabel={'가벼워요'} maxLabel={'진해요'} value={0} onChange={handleLightBoldChange} name='바디감' isDraggable={true} size='small' reset={resetTrigger} />
<ControlBar label='타닌' minLabel={'부드러워요'} maxLabel={'떫어요'} value={0} onChange={handleSmoothTannicChange} name='타닌' isDraggable={true} size='small' reset={resetTrigger} />
<ControlBar label='당도' minLabel={'드라이해요'} maxLabel={'달아요'} value={0} onChange={handleDrySweetChange} name='당도' isDraggable={true} size='small' reset={resetTrigger} />
<ControlBar label='산미' minLabel={'안셔요'} maxLabel={'많이셔요'} value={0} onChange={handleSoftAcidicChange} name='산미' isDraggable={true} size='small' reset={resetTrigger} />
<div className='flex h-[212px] flex-col gap-6 mobile:h-[194px]'>
<h4 className='text-xl font-bold text-gray-800 mobile:text-2lg'>와인의 맛은 어땠나요?</h4>
<div>
<ControlBar
label='바디감'
minLabel={'가벼워요'}
maxLabel={'진해요'}
value={0}
onChange={handleLightBoldChange}
name='바디감'
isDraggable={true}
size='small'
reset={resetTrigger}
/>
<ControlBar
label='타닌'
minLabel={'부드러워요'}
maxLabel={'떫어요'}
value={0}
onChange={handleSmoothTannicChange}
name='타닌'
isDraggable={true}
size='small'
reset={resetTrigger}
/>
<ControlBar label='당도' minLabel={'드라이해요'} maxLabel={'달아요'} value={0} onChange={handleDrySweetChange} name='당도' isDraggable={true} size='small' reset={resetTrigger} />
<ControlBar label='산미' minLabel={'안셔요'} maxLabel={'많이셔요'} value={0} onChange={handleSoftAcidicChange} name='산미' isDraggable={true} size='small' reset={resetTrigger} />
</div>
</div>
</div>
<div className='flex flex-col gap-6'>
<h4 className='text-xl font-bold text-gray-800 mobile:text-2lg'>기억에 남는 향이 있나요?</h4>
<div className='flex flex-wrap gap-[10px]'>
{aromas.map((aroma) => (
<button
key={aroma.key}
type='button'
onClick={() => handleAromaClick(aroma.key)}
className={`rounded-full border border-gray-300 px-[18px] py-[10px] font-medium mobile:px-[10px] mobile:py-[6px] mobile:text-md ${selectedAroma.includes(aroma.key) ? 'border border-purple-100 bg-purple-100 text-white' : 'border border-gray-300 bg-white text-gray-800'}`}
>
{aroma.name}
</button>
))}
<div className='flex flex-col gap-6'>
<h4 className='text-xl font-bold text-gray-800 mobile:text-2lg'>기억에 남는 향이 있나요?</h4>
<div className='flex flex-wrap gap-[10px]'>
{aromas.map((aroma) => (
<button
key={aroma.key}
type='button'
onClick={() => handleAromaClick(aroma.key)}
className={`rounded-full border border-gray-300 px-[18px] py-[10px] font-medium mobile:px-[10px] mobile:py-[6px] mobile:text-md ${selectedAroma.includes(aroma.key) ? 'border border-purple-100 bg-purple-100 text-white' : 'border border-gray-300 bg-white text-gray-800'}`}
>
{aroma.name}
</button>
))}
</div>
</div>
</div>
</div>
<Button
text='리뷰 남기기'
type='submit'
variant='primary'
disabled={aromaValue.length === 0 || ratingValue === 0 || !textValue.trim()}
className='mt-12 h-[54px] w-auto whitespace-nowrap rounded-xl text-center text-lg disabled:bg-gray-400 pc:w-[480px] pc:px-[203.5px] pc:py-[14px] tablet:w-[480px] mobile:mb-8 mobile:w-full mobile:min-w-[300px]'
/>
</form>
<Button
text='리뷰 남기기'
type='submit'
variant='primary'
disabled={aromaValue.length === 0 || ratingValue === 0 || !textValue.trim()}
className='mt-12 h-[54px] w-auto whitespace-nowrap rounded-xl text-center text-lg disabled:bg-gray-400 pc:w-[480px] pc:px-[203.5px] pc:py-[14px] tablet:w-[480px] mobile:mb-8 mobile:w-full mobile:min-w-[300px]'
/>
</form>
</div>
</div>
</Modal>
</div>
Expand Down
Loading