Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
35 changes: 35 additions & 0 deletions src/components/my-page/reviewing-modal/button-hearts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useState } from 'react';
import Image from 'next/image';
import activeHeart from '@/public/assets/icons/active-heart.svg';
import defaultHeart from '@/public/assets/icons/default-heart.svg';

interface ButtonHeartsProps {
onChange: (score: number) => void;
}

export default function ButtonHearts({ onChange }: ButtonHeartsProps) {
const [clickedArray, setClickedArray] = useState<boolean[]>(Array(5).fill(false));

const handleClick = (index: number) => {
setClickedArray((prev) => prev.map((_, i) => i <= index));
onChange(index + 1);
};
Comment on lines +13 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๊ฐœ์„  ํ•„์š”

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์•ˆ์ •์„ฑ๊ณผ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ ๊ฐœ์„ ์‚ฌํ•ญ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค:

  • ์ ์ˆ˜ ๊ณ„์‚ฐ ๋กœ์ง์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ
  • ํƒ€์ž… ์•ˆ์ •์„ฑ ๊ฐ•ํ™”
+const calculateScore = (index: number): 1 | 2 | 3 | 4 | 5 => {
+  return (index + 1) as 1 | 2 | 3 | 4 | 5;
+};
+
-  const handleClick = (index: number) => {
+  const handleClick = (index: 0 | 1 | 2 | 3 | 4) => {
     setClickedArray((prev) => prev.map((_, i) => i <= index));
-    onChange(index + 1);
+    onChange(calculateScore(index));
   };
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleClick = (index: number) => {
setClickedArray((prev) => prev.map((_, i) => i <= index));
onChange(index + 1);
};
const calculateScore = (index: number): 1 | 2 | 3 | 4 | 5 => {
return (index + 1) as 1 | 2 | 3 | 4 | 5;
};
const handleClick = (index: 0 | 1 | 2 | 3 | 4) => {
setClickedArray((prev) => prev.map((_, i) => i <= index));
onChange(calculateScore(index));
};


return (
<div className="flex w-full flex-col items-start justify-between gap-[12px]">
<p className="font-base font-semibold">๋งŒ์กฑ์Šค๋Ÿฌ์šด ๊ฒฝํ—˜์ด์—ˆ๋‚˜์š”?</p>
<div>
{Array.from({ length: 5 }).map((_, index) => (
<button type="button" onClick={() => handleClick(index)} key={`btn ${index + 1}`}>
<Image
src={clickedArray[index] ? activeHeart : defaultHeart}
width={24}
height={24}
alt="heartbtn"
/>
</button>
))}
</div>
</div>
);
Comment on lines +18 to +34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

์ ‘๊ทผ์„ฑ ๊ฐœ์„  ํ•„์š”

์›น ์ ‘๊ทผ์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•œ ๊ฐœ์„ ์‚ฌํ•ญ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค:

  • aria-label ์ถ”๊ฐ€
  • ๋” ๋ช…ํ™•ํ•œ ์ด๋ฏธ์ง€ ๋Œ€์ฒด ํ…์ŠคํŠธ
  • ํ˜„์žฌ ์„ ํƒ๋œ ์ ์ˆ˜ ์ƒํƒœ ์Œ์„ฑ ์•ˆ๋‚ด
 <div className="flex w-full flex-col items-start justify-between gap-[12px]">
-  <p className="font-base font-semibold">๋งŒ์กฑ์Šค๋Ÿฌ์šด ๊ฒฝํ—˜์ด์—ˆ๋‚˜์š”?</p>
+  <p className="font-base font-semibold" id="rating-description">๋งŒ์กฑ์Šค๋Ÿฌ์šด ๊ฒฝํ—˜์ด์—ˆ๋‚˜์š”?</p>
   <div>
     {Array.from({ length: 5 }).map((_, index) => (
       <button
         type="button"
         onClick={() => handleClick(index)}
         key={`btn ${index + 1}`}
+        aria-label={`${index + 1}์  ์„ ํƒ`}
+        aria-pressed={clickedArray[index]}
+        aria-describedby="rating-description"
       >
         <Image
           src={clickedArray[index] ? activeHeart : defaultHeart}
           width={24}
           height={24}
-          alt="heartbtn"
+          alt={clickedArray[index] ? '์ฑ„์›Œ์ง„ ํ•˜ํŠธ' : '๋นˆ ํ•˜ํŠธ'}
         />
       </button>
     ))}
   </div>
 </div>
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<div className="flex w-full flex-col items-start justify-between gap-[12px]">
<p className="font-base font-semibold">๋งŒ์กฑ์Šค๋Ÿฌ์šด ๊ฒฝํ—˜์ด์—ˆ๋‚˜์š”?</p>
<div>
{Array.from({ length: 5 }).map((_, index) => (
<button type="button" onClick={() => handleClick(index)} key={`btn ${index + 1}`}>
<Image
src={clickedArray[index] ? activeHeart : defaultHeart}
width={24}
height={24}
alt="heartbtn"
/>
</button>
))}
</div>
</div>
);
return (
<div className="flex w-full flex-col items-start justify-between gap-[12px]">
<p className="font-base font-semibold" id="rating-description">๋งŒ์กฑ์Šค๋Ÿฌ์šด ๊ฒฝํ—˜์ด์—ˆ๋‚˜์š”?</p>
<div>
{Array.from({ length: 5 }).map((_, index) => (
<button
type="button"
onClick={() => handleClick(index)}
key={`btn ${index + 1}`}
aria-label={`${index + 1}์  ์„ ํƒ`}
aria-pressed={clickedArray[index]}
aria-describedby="rating-description"
>
<Image
src={clickedArray[index] ? activeHeart : defaultHeart}
width={24}
height={24}
alt={clickedArray[index] ? '์ฑ„์›Œ์ง„ ํ•˜ํŠธ' : '๋นˆ ํ•˜ํŠธ'}
/>
</button>
))}
</div>
</div>
);

}
70 changes: 70 additions & 0 deletions src/components/my-page/reviewing-modal/review-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import Button from '@/src/components/common/button';
import Textarea from '@/src/components/common/input/textarea';
import ButtonHearts from './button-hearts';

type FormValues = {
reviewText: string;
score: number;
};

interface ReviewProps {
onCancel: () => void;
}

export default function ReviewForm({ onCancel }: ReviewProps) {
const { register, handleSubmit } = useForm<FormValues>();
const [textReview, setTextReview] = useState<string>('');
const [point, setPoint] = useState<number>(0);

Comment on lines +16 to +20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ› ๏ธ Refactor suggestion

ํผ ์ดˆ๊ธฐํ™” ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์„ค์ • ๊ฐœ์„  ํ•„์š”

useForm ํ›…์˜ ๊ธฐ๋ณธ ์˜ต์…˜์ด ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์‚ฌํ•ญ๋“ค์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • defaultValues ์„ค์ •
  • mode ์„ค์ • (์˜ˆ: onChange)
  • ํ•„์ˆ˜ ํ•„๋“œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
-  const { register, handleSubmit } = useForm<FormValues>();
+  const { register, handleSubmit } = useForm<FormValues>({
+    defaultValues: {
+      reviewText: '',
+      score: 0
+    },
+    mode: 'onChange'
+  });

Committable suggestion skipped: line range outside the PR's diff.

// TODO : ์ฃผ์„ ๋ถ€๋ถ„(api ์—ฐ๊ฒฐ) ์ˆ˜์ •
// TODO : form์— ๋„ฃ๊ธฐ: onSubmit={handleSubmit(clickSubmit)}

const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setTextReview(e.target.value);
};

const handleScoreChange = (newScore: number) => {
setPoint(newScore);
};

return (
<form className="flex h-[308px] w-[472px] flex-col justify-between gap-[24px]">
<ButtonHearts onChange={handleScoreChange} />
<Textarea
placeholder="๋‚จ๊ฒจ์ฃผ์‹  ๋ฆฌ๋ทฐ๋Š” ํ”„๋กœ๊ทธ๋žจ ์šด์˜ ๋ฐ ๋‹ค๋ฅธ ํšŒ์› ๋ถ„๋“ค๊ป˜ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค."
inputClassNames="w-[471px] h-[120px] bg-gray-50 text-gray-400"
value={textReview}
onChange={handleTextChange}
register={register('reviewText')}
label="๊ฒฝํ—˜์— ๋Œ€ํ•ด ๋‚จ๊ฒจ์ฃผ์„ธ์š”"
styles={{
input: {
'::placeholder': {
color: 'gray - 400',
fontWeight: 500,
fontSize: '1rem',
},
},
label: {
fontWeight: 600,
fontSize: '1rem',
},
}}
/>
<input type="hidden" value={point} {...register('score')} />
<div className="font-base flex justify-between gap-[16px] font-semibold">
<Button
onClick={onCancel}
className="h-[44px] w-[228px] border border-blue-500 text-blue-500"
>
์ทจ์†Œ
</Button>
<Button type="submit" className="h-[44px] w-[228px] border-none bg-blue-500 text-white">
๋ฆฌ๋ทฐ ๋“ฑ๋ก
</Button>
</div>
</form>
);
}
41 changes: 41 additions & 0 deletions src/components/my-page/reviewing-modal/reviewing-modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useEffect, useState } from 'react';
import { Button } from '@mantine/core';
import { action } from '@storybook/addon-actions';
import { Meta, StoryFn } from '@storybook/react';
import ReviewingModal, { ReviewingModalProps } from './reviewing-modal';

const meta: Meta<typeof ReviewingModal> = {
title: 'Modal/ReviewingModal',
component: ReviewingModal,
argTypes: { opened: { control: 'boolean' } },
};

export default meta;
const Template: StoryFn<ReviewingModalProps> = function GatheringDetailModalStory({
opened,
}: ReviewingModalProps) {
const [isOpened, setIsOpened] = useState(opened);

const handleOpen = () => {
action('Modal Opened')();
setIsOpened(true);
};

const handleClose = () => {
action('Modal Closed')();
setIsOpened(false);
};

useEffect(() => {
setIsOpened(opened);
}, [opened]);

return (
<>
<Button onClick={handleOpen}>Open Modal</Button>
<ReviewingModal opened={isOpened} close={handleClose} />
</>
);
};

export const Default = Template.bind({});
36 changes: 36 additions & 0 deletions src/components/my-page/reviewing-modal/reviewing-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';

import React from 'react';
import { Modal } from '@mantine/core';
import ReviewForm from './review-form';

export interface ReviewingModalProps {
opened: boolean;
close: () => void;
}

export default function ReviewingModal({ opened, close }: ReviewingModalProps) {
return (
<Modal
opened={opened}
centered
title="๋ฆฌ๋ทฐ ์“ฐ๊ธฐ"
onClose={close}
size="auto"
padding={24}
radius={12}
overlayProps={{ backgroundOpacity: 0.5 }}
styles={{
title: {
fontWeight: 600,
fontSize: '1.125rem',
},
body: {
fontFamily: 'var(--font-pretendard)',
},
}}
>
<ReviewForm onCancel={close} />
</Modal>
);
}