Skip to content

feat: 예약 메일 API 연동#111

Merged
hyeeuncho merged 11 commits intodevelopfrom
feat/#110/reservation-email
May 6, 2025
Merged

feat: 예약 메일 API 연동#111
hyeeuncho merged 11 commits intodevelopfrom
feat/#110/reservation-email

Conversation

@hyeeuncho
Copy link
Copy Markdown
Member

@hyeeuncho hyeeuncho commented Apr 30, 2025

예약 메일 발송

스크린샷 2025-04-30 오후 7 03 47

예약 메일 조회 (발송/미발송)

스크린샷 2025-04-30 오후 7 04 23

예약 메일 수정

스크린샷 2025-04-30 오후 7 04 40

예약 메일 삭제

스크린샷 2025-04-30 오후 7 04 54

티켓별 이메일 보내기 구현

스크린샷 2025-05-03 오후 3 24 33

Summary by CodeRabbit

  • 신규 기능

    • 예약 이메일 관리 기능(읽기, 발송, 수정, 삭제) 추가 및 관련 API 연동
    • 이메일 예약 발송을 위한 상태 관리 및 전역 스토어 도입
    • 이메일 입력, 수정, 발송 UI에 예약 날짜/시간 선택 및 검증 기능 추가
    • 메일함에서 상태별(대기/발송) 이메일 목록 조회 및 삭제 기능 제공
    • 참가자 및 티켓 정보 연동을 통한 이메일 수신자 관리 기능 강화
  • 버그 수정

    • 이메일 입력 및 발송 시 필수 항목 누락 시 알림 추가
  • 리팩터링

    • 이메일 입력, 수정, 발송 컴포넌트의 상태 관리를 전역 스토어로 통합
    • 메일함 페이지의 데이터 조회 및 삭제 로직을 비동기 처리로 개선
    • 이메일 관련 타입 및 인터페이스 구조 정비
    • 이메일 편집 및 삭제 기능의 상태 관리 및 라우팅 개선
    • 티켓 선택 모달 및 관련 컴포넌트의 데이터 연동 및 이벤트 핸들링 단순화
    • 메일 카드 컴포넌트의 상태 관리 및 라우팅 방식 개선
  • 스타일/사용성 개선

    • 시간 선택 컴포넌트가 상위 컴포넌트와 동기화되도록 개선
    • 메일 카드 및 모달의 UI/UX 개선
  • 문서화

    • 이메일 및 참가자 관련 타입 및 상태 구조 명확화
  • 기타

    • 일부 불필요한 코드 및 속성 제거, 타입 명시 강화
    • 인증 콜백 및 사용자 정보 요청에서 불필요한 필드 제거 및 처리 로직 개선

@hyeeuncho hyeeuncho added the 🔧 Feature 기능 구현 label Apr 30, 2025
@hyeeuncho hyeeuncho self-assigned this Apr 30, 2025
@hyeeuncho hyeeuncho linked an issue Apr 30, 2025 that may be closed by this pull request
4 tasks
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 30, 2025

"""

Walkthrough

이번 변경 사항에서는 예약 메일(이메일) 기능이 대대적으로 도입 및 통합되었습니다. 예약 메일의 생성, 조회, 수정, 삭제를 위한 API 모듈과 타입 정의가 추가되었고, 상태 관리를 위한 Zustand 스토어와 React Query 기반의 커스텀 훅이 구현되었습니다. 여러 UI 컴포넌트 및 페이지가 이 새로운 상태 관리와 API 연동 구조에 맞게 리팩토링되었습니다. 메일 입력, 전송, 편집, 삭제, 시간 선택 등의 기능이 중앙 집중적으로 관리되며, 기존의 로컬 상태 사용 방식이 스토어 기반으로 전환되었습니다.

Changes

파일/경로 요약 변경 요약
src/features/dashboard/api/mail.ts 예약 메일의 생성, 조회, 수정, 삭제 API 함수 추가 (readEmail, readPurchaserEmails, sendEmail, editEmail, deleteEmail)
src/features/dashboard/hook/useEmailHook.ts 예약 메일 관련 React Query 기반 커스텀 훅 5종 추가 (조회, 구매자 이메일 조회, 생성, 수정, 삭제)
src/features/dashboard/model/EmailStore.tsx 예약 메일 상태 관리용 Zustand 스토어 및 setter, reset 함수 추가
src/features/dashboard/model/emailInformation.ts 예약 메일 관련 타입 정의(EmailRequest, EmailResponse, ReadEmailResponse) 추가
src/features/dashboard/ui/EmailInput.tsx 이메일 입력 컴포넌트가 Zustand 스토어 기반으로 리팩토링, isEdited prop 추가, 로컬 상태 제거
src/features/event-manage/event-create/ui/TimePicker.tsx TimePicker가 onChange 콜백 prop을 받는 컨트롤드 컴포넌트로 변경
src/features/join/model/userInformation.ts UserInfoRequest 타입에서 id 필드 제거
src/pages/dashboard/ui/ParticipantsMangementPage.tsx map 함수 파라미터에 타입 명시 추가
src/pages/dashboard/ui/mail/EmailEditPage.tsx 메일 편집 페이지가 스토어 및 API 기반으로 리팩토링, TimePicker 콜백 연동, handleEdit 함수 도입
src/pages/dashboard/ui/mail/EmailPage.tsx 메일 생성 페이지가 스토어 및 API 기반으로 리팩토링, TimePicker 콜백 연동, handleSend 함수 도입
src/pages/dashboard/ui/mail/MailBoxPage.tsx 메일함 페이지가 useReadEmail, useDeleteEmail 훅 기반으로 리팩토링, 삭제 모달 및 비동기 처리 추가
src/pages/dashboard/ui/ticket/TicketListPage.tsx eventId의 기본값이 1에서 0으로 변경
src/pages/join/AuthCallback.tsx isModalOpen 관련 코드 및 의존성 제거, 데이터 없을 시 early return 로직 추가
src/pages/join/InfoInputPage.tsx 제출 데이터에서 id 필드 제거
src/widgets/dashboard/ui/EmailModal.tsx EmailModal이 스토어 및 API 기반으로 리팩토링, TimePicker 연동, handleSend 함수 추가
src/widgets/dashboard/ui/SentMailCard.tsx mail prop 타입 변경, 삭제/수정 버튼 로직 리팩토링, 스토어 연동, 동적 라우팅 적용
src/features/dashboard/model/participantInformation.ts ParticipantData 인터페이스 추가
src/widgets/dashboard/ui/SelectTicketInfo.tsx props 간소화 및 onClick 콜백 통합, tickets 타입 변경
src/widgets/dashboard/ui/SelectTicketModal.tsx eventId 기반 티켓 데이터 비동기 로딩, useTickets 훅 사용, 구매자 이메일 조회 및 상태 업데이트 로직 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant EmailInput
    participant EmailStore
    participant TimePicker
    participant useSendEmail/useEditEmail
    participant API(mail.ts)
    participant MailBoxPage

    User->>EmailInput: 제목/내용/수신자 입력
    EmailInput->>EmailStore: 상태 업데이트
    User->>TimePicker: 날짜/시간 선택
    TimePicker->>EmailStore: 날짜/시간 업데이트
    User->>EmailInput: "보내기" 클릭
    EmailInput->>useSendEmail/useEditEmail: 메일 데이터 전달
    useSendEmail/useEditEmail->>API(mail.ts): HTTP 요청 (생성/수정)
    API(mail.ts)-->>useSendEmail/useEditEmail: 응답 반환
    useSendEmail/useEditEmail->>MailBoxPage: 성공 시 이동/알림
Loading

Suggested reviewers

  • Yejiin21

Poem

🐇
메일이 예약되어 토끼도 신이 나,
상태와 시간, 수신자까지 다~
버튼 한 번이면 쏙쏙 전송,
API와 스토어가 한마음 한몸!
메일함 가득 소식이 오면
토끼는 깡충, 기쁨이 두 배!
이메일 세상, 오늘도 평화~

"""

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🔭 Outside diff range comments (1)
src/pages/dashboard/ui/mail/MailBoxPage.tsx (1)

31-31: 💡 Verification agent

🧩 Analysis chain

검색 기능이 구현되지 않은 것 같습니다.

SearchBar 컴포넌트가 있지만 실제 검색 기능이 구현되지 않은 것으로 보입니다. 필터링 로직을 추가하거나 아직 구현 예정이라면 TODO 주석을 추가하는 것이 좋습니다.


🏁 Script executed:

#!/bin/bash
# SearchBar 컴포넌트의 검색 기능 구현 확인

# SearchBar 컴포넌트 코드 찾기
rg -A 5 "SearchBar" "src/shared/ui/SearchBar.tsx" || echo "SearchBar 컴포넌트 파일을 찾을 수 없습니다."

# 검색어 상태 및 필터링 로직이 있는지 확인
rg -A 10 "const \[search" "src/pages/dashboard/ui/mail/MailBoxPage.tsx" || echo "검색어 상태가 정의되지 않았습니다."

Length of output: 548


검색 기능 로직 미구현 – 상태 관리 및 필터링 또는 TODO 주석 추가 필요

MailBoxPage.tsx에서 SearchBar가 렌더링만 되고 실제 검색어 상태(state) 관리나 필터링 로직이 없습니다. 다음 중 하나를 적용해주세요:

  • search 상태 및 setSearch 정의
  • SearchBarvalue={search}onChange={…} 전달
  • 메일 목록을 search 값으로 필터링하는 로직 추가
  • 아직 구현 중이라면 TODO 주석↑ 삽입

예시:

-import <SearchBar placeholder="제목 검색" className="w-[35%] my-2" />
+const [search, setSearch] = useState("");
+
+<SearchBar
+  placeholder="제목 검색"
+  className="w-[35%] my-2"
+  value={search}
+  onChange={setSearch}
/>
🧹 Nitpick comments (13)
src/features/event-manage/event-create/ui/TimePicker.tsx (1)

16-25: 날짜와 시간 형식 개선 가능

형식화된 날짜와 시간을 상위 컴포넌트에 전달하는 방식은 좋지만, 시간 형식이 'HH:mm'으로 표준화되어 있지 않을 수 있습니다.

시간 형식을 항상 표준화된 형태로 유지하도록 수정을 제안합니다:

useEffect(() => {
-  const formattedTime = `${selectedHour}:${selectedMinute}`;
+  // HH:mm 형식 보장
+  const formattedTime = `${selectedHour.padStart(2, '0')}:${selectedMinute.padStart(2, '0')}`;
  onTimeChange(formattedTime);
}, [selectedHour, selectedMinute]);
src/features/dashboard/model/EmailStore.tsx (1)

25-25: 통합 계획에 대한 설명 필요

주석에 "추후 2개 통합 예정"이라고 되어 있는데, 이 내용은 코드에서 직접적으로 전달되지 않습니다.

주석을 좀 더 명확하게 작성하는 것이 좋겠습니다:

- reservationTime: '', // 추후 2개 통합 예정
+ reservationTime: '', // 추후 reservationDate와 통합하여 ISO 형식의 단일 필드로 변경 예정
src/pages/dashboard/ui/mail/MailBoxPage.tsx (1)

52-53: 불필요한 삼항 연산자 사용을 개선하세요.

불필요한 boolean 리터럴이 있는 삼항 연산자를 사용하고 있습니다. 더 간결하게 작성할 수 있습니다.

-              isPending={listType === 'pending' ? true : false}
+              isPending={listType === 'pending'}
src/pages/dashboard/ui/mail/EmailEditPage.tsx (2)

11-59: 폼 유효성 검사 추가를 고려하세요.

현재 구현에는 이메일 데이터 제출 전에 필수 필드가 입력되었는지 확인하는 유효성 검사가 없습니다. 제목, 내용, 수신자 등의 필수 필드에 대한 검증을 추가하는 것이 좋을 것 같습니다.

다음과 같은 유효성 검사 로직을 handleEdit 함수에 추가할 수 있습니다:

  const handleEdit = () => {
    const eventId = id ? parseInt(id) : 0;
+   // 필수 필드 유효성 검사
+   if (!title.trim()) {
+     alert('제목을 입력해주세요.');
+     return;
+   }
+   if (!content.trim()) {
+     alert('내용을 입력해주세요.');
+     return;
+   }
+   if (recipients.length === 0) {
+     alert('받는 사람을 추가해주세요.');
+     return;
+   }
+   if (!reservationDate || !reservationTime) {
+     alert('예약 날짜와 시간을 선택해주세요.');
+     return;
+   }
+
    const emailData = {
      eventId,
      title,
      content,
      recipients,
      reservationDate,
      reservationTime,
    };
    editEmail({ reservationEmailId: reservationEmailId, data: emailData,});
  };
🧰 Tools
🪛 ESLint

[error] 47-47: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)


43-57: 로딩 상태 표시 기능을 추가하는 것이 좋을 것 같습니다.

editEmail 뮤테이션이 진행 중일 때 로딩 상태를 표시하는 기능이 없습니다. 사용자 경험 개선을 위해 버튼 비활성화나 로딩 인디케이터를 추가하는 것이 좋을 것 같습니다.

다음과 같이 로딩 상태를 추가하고 버튼을 비활성화할 수 있습니다:

  const { id } = useParams();
- const {mutate: editEmail} = useEditEmail();
+ const {mutate: editEmail, isPending} = useEditEmail();

...

  return (
    <DashboardLayout centerContent="WOOACON 2024">
      <div className="flex flex-col gap-5 mt-8 px-7">
        <EmailInput
          type="이메일 내용 수정"
          openSelectTicket={() => setTicketModalOpen(true)}
          allParticipantEmails={participants.map((p: { email: string; }) => p.email)}
          isEdited= {true}
        />
        {/*시간 선택 컴포넌트*/}
        <TimePicker
          onTimeChange={(time: string) => setReservationTime(time)}
          onDateChange={(date: string) => setReservationDate(date)}
        />
-       <Button label="보내기" onClick={handleEdit} className="w-full h-12 rounded-full" />
+       <Button 
+         label={isPending ? "처리 중..." : "보내기"} 
+         onClick={handleEdit} 
+         className="w-full h-12 rounded-full"
+         disabled={isPending} 
+       />
      </div>
      {ticketModalOpen && <SelectTicketModal onClose={() => setTicketModalOpen(false)} />}
    </DashboardLayout>
  );
🧰 Tools
🪛 ESLint

[error] 47-47: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/features/dashboard/ui/EmailInput.tsx (2)

27-33: 초기화 로직에 들여쓰기 오류가 있습니다.

useEffect 내의 setContent 호출 들여쓰기가 일관되지 않습니다. 코드 가독성을 위해 수정이 필요합니다.

  useEffect(() => {
    if (!isEdited) {
      setRecipients([]); 
      setTitle('');
-    setContent('');
+      setContent('');
    }
  }, [isEdited, setRecipients]);

34-40: 이메일 주소 유효성 검사 추가를 고려하세요.

이메일을 추가할 때 형식 검사를 수행하는 로직이 없습니다. 잘못된 이메일 주소가 추가되는 것을 방지하기 위해 유효성 검사를 추가하는 것이 좋을 것 같습니다.

이메일 주소 검증 함수를 추가하고 이메일 추가 시 호출하는 방법을 고려해 보세요:

const isValidEmail = (email: string): boolean => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
};

// EmailInputBase 컴포넌트에 onAddEmail 프롭을 추가하고, 
// 해당 프롭을 통해 이메일 추가 전 유효성 검사를 수행하도록 수정
src/features/dashboard/api/mail.ts (3)

17-20: sendEmail 함수의 반환 타입을 명시적으로 지정해주세요.

다른 함수와의 일관성을 위해 반환 타입을 명시적으로 지정하는 것이 좋습니다.

-export const sendEmail = async (data: EmailRequest) => {
+export const sendEmail = async (data: EmailRequest): Promise<EmailResponse> => {
    const response = await axiosClient.post('/reservation-emails', data);
    return response.data.result;
};

22-25: editEmail 함수의 반환 타입을 명시적으로 지정해주세요.

함수 시그니처의 일관성을 위해 반환 타입을 추가하는 것이 좋습니다.

-export const editEmail = async (reservationEmailId: number, data: EmailRequest) => {
+export const editEmail = async (reservationEmailId: number, data: EmailRequest): Promise<EmailResponse> => {
    const response = await axiosClient.put(`/reservation-emails/${reservationEmailId}`, data);
    return response.data.result;
};

27-30: deleteEmail 함수의 반환 타입을 명시적으로 지정해주세요.

반환 타입을 명시하면 API 함수 호출 시 타입 안전성이 향상됩니다.

-export const deleteEmail = async (reservationEmailId: number) => {
+export const deleteEmail = async (reservationEmailId: number): Promise<EmailResponse> => {
    const response = await axiosClient.delete(`/reservation-emails/${reservationEmailId}`);
    return response.data.result;
}
src/features/dashboard/hook/useEmailHook.ts (3)

15-30: 네이티브 alert 대신 더 나은 알림 컴포넌트 사용을 고려해 보세요.

alert() 함수는 사용자 경험이 좋지 않습니다. Toast 또는 모달 알림 컴포넌트를 사용하는 것이 좋습니다.

또한 실패 처리 시 에러 세부 정보를 보여주면 디버깅에 도움이 될 수 있습니다.

export const useSendEmail = () => {
    const { id } = useParams();
    const navigate = useNavigate();
    const { reset } = useEmailStore();
    return useMutation<EmailResponse, Error, EmailRequest>({
        mutationFn: sendEmail,
        onSuccess: () => {
            reset();
-           alert("예약 메일이 성공적으로 발송되었습니다!");
+           toast.success("예약 메일이 성공적으로 발송되었습니다!");
            navigate(`/dashboard/${id}/mailBox`);
        },
-       onError: () => {
-           alert("메일 전송에 실패했습니다. 다시 시도해 주세요.");
+       onError: (error) => {
+           toast.error(`메일 전송에 실패했습니다: ${error.message}. 다시 시도해 주세요.`);
        },
    });
};

32-47: useEditEmail 훅의 에러 처리가 개선될 수 있습니다.

에러 객체의 정보를 활용하여 더 구체적인 에러 메시지를 표시하는 것이 좋습니다.

        onSuccess: () => {
            reset();
-           alert("예약 메일이 성공적으로 수정되었습니다!");
+           toast.success("예약 메일이 성공적으로 수정되었습니다!");
            navigate(`/dashboard/${id}/mailBox`);
        },
-       onError: () => {
-           alert("메일 수정에 실패했습니다. 다시 시도해 주세요.");
+       onError: (error) => {
+           toast.error(`메일 수정에 실패했습니다: ${error.message}. 다시 시도해 주세요.`);
        },

1-60: 재시도 로직 추가를 고려해보세요.

네트워크 요청이 일시적으로 실패할 수 있으므로, React Query의 재시도 기능을 활용하는 것이 좋습니다. 특히 읽기 작업에 유용합니다.

예를 들어 useReadEmail에 retry 옵션을 추가할 수 있습니다:

export const useReadEmail = (eventId: number, status: 'PENDING' | 'SENT') => {
    return useQuery<ReadEmailResponse[]>({
        queryKey: ['emails', eventId, status],
        queryFn: () => readEmail(eventId, status),
        enabled: !!eventId && !!status,
+       retry: 2,
+       retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 10000),
    });
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4694f99 and 329c80a.

📒 Files selected for processing (16)
  • src/features/dashboard/api/mail.ts (1 hunks)
  • src/features/dashboard/hook/useEmailHook.ts (1 hunks)
  • src/features/dashboard/model/EmailStore.tsx (1 hunks)
  • src/features/dashboard/model/emailInformation.ts (1 hunks)
  • src/features/dashboard/ui/EmailInput.tsx (2 hunks)
  • src/features/event-manage/event-create/ui/TimePicker.tsx (1 hunks)
  • src/features/join/model/userInformation.ts (0 hunks)
  • src/pages/dashboard/ui/ParticipantsMangementPage.tsx (1 hunks)
  • src/pages/dashboard/ui/mail/EmailEditPage.tsx (1 hunks)
  • src/pages/dashboard/ui/mail/EmailPage.tsx (1 hunks)
  • src/pages/dashboard/ui/mail/MailBoxPage.tsx (2 hunks)
  • src/pages/dashboard/ui/ticket/TicketListPage.tsx (1 hunks)
  • src/pages/join/AuthCallback.tsx (2 hunks)
  • src/pages/join/InfoInputPage.tsx (0 hunks)
  • src/widgets/dashboard/ui/EmailModal.tsx (2 hunks)
  • src/widgets/dashboard/ui/SentMailCard.tsx (2 hunks)
💤 Files with no reviewable changes (2)
  • src/features/join/model/userInformation.ts
  • src/pages/join/InfoInputPage.tsx
🧰 Additional context used
🧬 Code Graph Analysis (8)
src/pages/join/AuthCallback.tsx (2)
src/app/provider/authStore.ts (1)
  • useAuthStore (17-33)
src/features/join/hooks/useUserHook.ts (1)
  • useUserInfo (5-10)
src/pages/dashboard/ui/mail/MailBoxPage.tsx (2)
src/features/dashboard/hook/useEmailHook.ts (2)
  • useReadEmail (7-13)
  • useDeleteEmail (49-60)
src/features/dashboard/api/mail.ts (1)
  • deleteEmail (27-30)
src/pages/dashboard/ui/mail/EmailEditPage.tsx (4)
src/features/dashboard/hook/useParticipants.ts (1)
  • useParticipants (9-27)
src/features/dashboard/api/mail.ts (1)
  • editEmail (22-25)
src/features/dashboard/hook/useEmailHook.ts (1)
  • useEditEmail (32-47)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (19-41)
src/pages/dashboard/ui/mail/EmailPage.tsx (4)
src/features/dashboard/hook/useParticipants.ts (1)
  • useParticipants (9-27)
src/features/dashboard/api/mail.ts (1)
  • sendEmail (17-20)
src/features/dashboard/hook/useEmailHook.ts (1)
  • useSendEmail (15-30)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (19-41)
src/widgets/dashboard/ui/EmailModal.tsx (3)
src/features/dashboard/api/mail.ts (1)
  • sendEmail (17-20)
src/features/dashboard/hook/useEmailHook.ts (1)
  • useSendEmail (15-30)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (19-41)
src/features/dashboard/hook/useEmailHook.ts (3)
src/features/dashboard/model/emailInformation.ts (3)
  • ReadEmailResponse (15-22)
  • EmailResponse (9-14)
  • EmailRequest (1-8)
src/features/dashboard/api/mail.ts (4)
  • readEmail (4-15)
  • sendEmail (17-20)
  • editEmail (22-25)
  • deleteEmail (27-30)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (19-41)
src/features/dashboard/ui/EmailInput.tsx (1)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (19-41)
src/features/dashboard/api/mail.ts (2)
src/features/dashboard/model/emailInformation.ts (2)
  • ReadEmailResponse (15-22)
  • EmailRequest (1-8)
src/shared/types/api/http-client.ts (1)
  • axiosClient (6-13)
🪛 ESLint
src/pages/dashboard/ui/ParticipantsMangementPage.tsx

[error] 56-56: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/pages/dashboard/ui/mail/EmailEditPage.tsx

[error] 47-47: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/pages/dashboard/ui/mail/EmailPage.tsx

[error] 47-47: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🪛 Biome (1.9.4)
src/pages/dashboard/ui/mail/MailBoxPage.tsx

[error] 53-54: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

🔇 Additional comments (36)
src/pages/join/AuthCallback.tsx (3)

10-10: 코드 정리: 사용하지 않는 상태 제거

사용하지 않는 isModalOpen 상태를 제거하여 코드가 더 간결해졌습니다. 불필요한 의존성을 줄이면 유지보수성이 향상됩니다.


15-16: 데이터 유효성 검사 추가로 안정성 향상

data가 없는 경우 빠르게 함수 실행을 중단하는 가드 조건을 추가한 것은 매우 좋은 방어적 프로그래밍 방식입니다. 이는 data가 로드되기 전에 접근하려는 시도를 방지하여 잠재적인 오류를 예방합니다.


31-31: 의존성 배열 업데이트 완료

useEffect의 의존성 배열에서 사용하지 않는 isModalOpen을 제거하여 불필요한 리렌더링을 방지하고 컴포넌트의 성능을 개선했습니다.

src/features/dashboard/model/emailInformation.ts (3)

1-8: 이메일 요청 인터페이스 구조 적절

EmailRequest 인터페이스는 이메일 전송에 필요한 모든 필드를 포함하고 있습니다. 이벤트 ID, 제목, 내용, 수신자 목록, 그리고 예약 날짜와 시간을 적절히 정의했습니다.

추후 개선 사항으로, 날짜와 시간 필드에 더 구체적인 포맷 패턴이나 유효성 검사를 추가하는 것을 고려해 볼 수 있습니다.


9-14: 응답 인터페이스 구조 명확

EmailResponse 인터페이스는 API 응답의 표준 구조를 잘 따르고 있습니다. 성공 여부, 코드, 메시지 및 결과를 포함하여 클라이언트가 응답을 처리하기 쉽게 설계되었습니다.


15-22: 이메일 조회 응답 인터페이스 적절

ReadEmailResponse 인터페이스는 기존 이메일을 조회할 때 필요한 모든 정보를 포함하고 있습니다. ID 필드가 추가된 것은 조회 데이터 구조에 적합합니다.

src/pages/dashboard/ui/ticket/TicketListPage.tsx (1)

16-16: 이벤트 ID 기본값 변경 확인 필요

이벤트 ID의 기본값이 1에서 0으로 변경되었습니다. 이 변경이 의도적인지, 그리고 API가 이 값을 올바르게 처리할 수 있는지 확인이 필요합니다.

다른 컴포넌트에서도 이벤트 ID 기본값으로 0을 사용하는지, 그리고 이 값이 백엔드 API와 호환되는지 확인해주세요. 이 변경으로 인해 티켓 데이터 조회에 문제가 없는지 테스트해볼 것을 권장합니다.

src/features/event-manage/event-create/ui/TimePicker.tsx (1)

5-26: Props 인터페이스 및 콜백 구현 완료

컴포넌트를 제어 가능한(controlled) 형태로 변경한 점이 훌륭합니다. 날짜와 시간 변경 시 상위 컴포넌트에 값을 전달하는 로직이 잘 구현되었습니다.

src/features/dashboard/model/EmailStore.tsx (1)

1-42: Zustand 스토어 구현 완료

이메일 관련 상태를 중앙 집중적으로 관리하는 스토어 구현이 잘 되었습니다. 필요한 상태와 액션들이 모두 포함되어 있습니다.

src/pages/dashboard/ui/mail/MailBoxPage.tsx (6)

7-8: API 연동 구현 완료 👍

예약 메일 조회와 삭제 기능을 API와 연동하기 위한 훅을 잘 import 했습니다.


11-12: URL 파라미터에서 이벤트 ID를 적절히 추출합니다.

이벤트 ID를 URL 파라미터에서 가져와 정수로 변환하는 로직이 적절합니다.


17-19: React Query 기반 데이터 페칭 구현 👏

상태에 따라 'PENDING' 또는 'SENT' 필터링을 적용하여 이메일 데이터를 가져오고, 삭제 뮤테이션을 준비하는 로직이 잘 구현되었습니다.


21-24: 이메일 삭제 핸들러 구현이 명확합니다.

삭제 함수가 간결하게 구현되었으며, 모달 상태 관리도 적절합니다.


45-58: 로딩 상태 처리와 조건부 렌더링이 잘 구현되었습니다.

API 로딩 상태에 따른 조건부 렌더링이 적절히 구현되었고, 이메일 목록을 매핑하여 표시하는 방식이 효율적입니다.

🧰 Tools
🪛 Biome (1.9.4)

[error] 53-54: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


65-70: 모달 닫기 및 삭제 로직 개선

모달을 닫을 때 selectedEmailId를 null로 리셋하고, 삭제 시 fallback 값으로 0을 사용하는 로직이 안전하게 구현되었습니다.

src/pages/dashboard/ui/mail/EmailEditPage.tsx (6)

14-15: 이벤트 ID 추출 및 수정 뮤테이션 설정이 적절합니다.

URL 파라미터에서 ID를 추출하고 useEditEmail 훅을 사용하여 이메일 수정 뮤테이션을 준비하는 로직이 잘 구현되었습니다.


17-26: 중앙 집중식 상태 관리 적용이 훌륭합니다.

useEmailStore를 통해 이메일 관련 상태와 상태 설정 함수들을 잘 가져왔습니다. 상태 관리가 중앙 집중화되어 코드 일관성이 향상되었습니다.


28-39: 이메일 수정 핸들러가 명확하게 구현되었습니다.

handleEdit 함수가 필요한 모든 필드를 포함하여 데이터를 구성하고 editEmail 뮤테이션을 호출하는 로직이 적절합니다.


48-48: 수정 모드 플래그 추가가 적절합니다.

isEdited 속성을 추가하여 이메일 수정 모드를 명확히 표시하는 것이 좋습니다.


51-54: 시간 선택 컴포넌트 콜백 구현이 잘 되었습니다.

TimePicker 컴포넌트에 시간과 날짜 변경에 대한 콜백을 추가하여 중앙 상태를 업데이트하는 로직이 잘 구현되었습니다.


55-55: 버튼 클릭 핸들러가 적절히 업데이트되었습니다.

"보내기" 버튼의 클릭 핸들러가 handleEdit 함수를 호출하도록 변경되어 API 연동이 잘 이루어졌습니다.

src/widgets/dashboard/ui/SentMailCard.tsx (5)

6-8: 필요한 import와 타입이 적절히 추가되었습니다.

라우팅, 타입 정의, 상태 관리를 위한 import가 적절히 추가되었습니다.


11-14: 타입 및 프롭 정의가 명확하게 업데이트되었습니다.

mail 프롭의 타입을 ReadEmailResponse로 변경하고, 모달 상태 설정 대신 onClickDelete 콜백 함수를 사용하도록 업데이트한 것이 좋습니다.


20-31: 이메일 수정 로직이 중앙 상태 관리와 잘 연동되었습니다.

useParams를 사용해 이벤트 ID를 가져오고, 스토어 상태를 업데이트한 후 편집 페이지로 이동하는 로직이 잘 구현되었습니다.


42-42: 날짜 표시 수정이 적절합니다.

mail.reservationDate를 사용하여 날짜를 표시하도록 변경되었습니다.


59-60: 버튼 핸들러가 적절히 업데이트되었습니다.

삭제 및 수정 버튼에 콜백 함수와 핸들러가 적절히 연결되었습니다.

Also applies to: 66-67

src/features/dashboard/ui/EmailInput.tsx (6)

1-1: 필요한 import가 적절히 추가되었습니다.

useEffect와 중앙 상태 관리를 위한 useEmailStore가 잘 추가되었습니다.

Also applies to: 6-6


12-15: 수정 모드 지원을 위한 프롭 추가가 적절합니다.

isEdited 옵션 프롭을 추가하여 컴포넌트가 편집 모드에서 동작할 수 있도록 확장한 점이 좋습니다.


18-25: 중앙 집중식 상태 관리 구현이 효과적입니다.

useEmailStore에서 필요한 상태와 설정 함수들을 가져와 사용하는 방식이 적절합니다. 이 접근법은 컴포넌트 간 상태 일관성을 유지하는 데 도움이 됩니다.


35-40: 이메일 관리 함수가 잘 업데이트되었습니다.

addAllEmails와 removeAllEmail 함수가 중앙 상태를 업데이트하도록 변경되었습니다.


56-62: 이메일 입력 컴포넌트 연동이 적절합니다.

EmailInputBase 컴포넌트가 중앙 상태의 recipients를 사용하고, 적절한 플레이스홀더를 표시하도록 구현되었습니다.


64-73: 입력 필드 상태 연동이 잘 구현되었습니다.

제목과 내용 입력 필드가 중앙 상태와 연동되어 값을 표시하고 업데이트하는 로직이 잘 구현되었습니다.

src/features/dashboard/api/mail.ts (2)

1-3: 적절한 타입 및 모듈 가져오기를 사용하고 있습니다.

필요한 HTTP 클라이언트와 이메일 관련 타입을 올바르게 가져오고 있습니다.


4-15: readEmail 함수가 명확한 반환 타입과 매개변수 타입을 가지고 있습니다.

이벤트 ID와 상태(PENDING 또는 SENT)에 따라 이메일을 조회하는 기능이 잘 구현되어 있습니다. 타입 안전성이 보장되어 있고 Promise 반환 타입이 명시적으로 지정되어 있습니다.

src/features/dashboard/hook/useEmailHook.ts (2)

1-6: 필요한 의존성과 모듈을 잘 가져오고 있습니다.

React Query, 커스텀 API 함수, 타입, React Router 및 Zustand 스토어를 적절하게 가져오고 있습니다.


7-13: useReadEmail 훅이 적절하게 구현되어 있습니다.

Query 키가 적절하게 설정되어 있고, 필수 파라미터가 있을 때만 쿼리가 활성화되도록 enabled 옵션이 잘 설정되어 있습니다.

@github-actions
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (5)
src/pages/dashboard/ui/mail/EmailPage.tsx (3)

15-56: 이메일 상태 관리 및 발송 로직이 효과적으로 구현되었습니다.

이메일 제목, 내용, 수신자, 예약 날짜 및 시간에 대한 유효성 검사가 추가되어 데이터 무결성을 보장합니다.


63-63: 🛠️ Refactor suggestion

명시적 타입 지정 필요

any 타입은 타입 안전성을 저하시킵니다. 이메일 속성에 명시적인 타입을 지정해주세요.

- allParticipantEmails={participants.map((p: { email: any; }) => p.email)}
+ allParticipantEmails={participants.map((p: { email: string; }) => p.email)}
🧰 Tools
🪛 ESLint

[error] 63-63: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)


71-71: 🛠️ Refactor suggestion

이메일 발송 중 로딩 상태 관리 필요

이메일 발송 중에 사용자가 중복으로 버튼을 클릭하는 것을 방지하기 위한 로딩 상태 관리가 필요합니다.

- const { mutate: sendEmail } = useSendEmail();
+ const { mutate: sendEmail, isLoading } = useSendEmail();

// ...

- <Button label="보내기" onClick={handleSend} className="w-full h-12 rounded-full" />
+ <Button 
+   label={isLoading ? "발송 중..." : "보내기"} 
+   onClick={handleSend} 
+   className="w-full h-12 rounded-full" 
+   disabled={isLoading}
+ />
src/widgets/dashboard/ui/EmailModal.tsx (2)

27-54: handleSend 함수가 적절하게 구현되었습니다.

이전 리뷰에서 제안한 대로 유효성 검사가 잘 구현되었습니다. 모든 필수 필드(제목, 내용, 수신자, 예약 날짜/시간)에 대한 검증이 적절히 포함되어 있습니다.


69-70: 🛠️ Refactor suggestion

이메일 발송 중 로딩 상태 처리가 필요합니다.

이메일 발송 중에 사용자가 중복으로 버튼을 클릭하는 것을 방지하는 로딩 상태 관리가 여전히 구현되지 않았습니다. useSendEmail 훅에서 제공하는 isLoading 상태를 활용하세요.

다음과 같이 로딩 상태를 사용하고 발송 중에는 버튼이 비활성화되도록 처리하세요:

- const { mutate: sendEmail } = useSendEmail();
+ const { mutate: sendEmail, isLoading } = useSendEmail();

// ...

- <TertiaryButton label="취소" type="button" color="black" size="medium" onClick={onClose} />
+ <TertiaryButton 
+   label="취소" 
+   type="button" 
+   color="black" 
+   size="medium" 
+   onClick={onClose} 
+   disabled={isLoading} 
+ />
- <TertiaryButton label="전송" type="button" color="pink" size="medium" onClick={handleSend} />
+ <TertiaryButton 
+   label={isLoading ? "발송 중..." : "전송"} 
+   type="button" 
+   color="pink" 
+   size="medium" 
+   onClick={handleSend} 
+   disabled={isLoading} 
+ />
🧹 Nitpick comments (1)
src/widgets/dashboard/ui/EmailModal.tsx (1)

44-44: eventId 파싱 시 안전성 강화가 필요합니다.

parseInt 함수는 문자열에 숫자가 아닌 문자가 포함되어 있을 경우 예기치 않은 결과를 반환할 수 있습니다. 파싱 오류 처리를 위한 추가 안전장치가 필요합니다.

다음과 같이 개선할 수 있습니다:

- const eventId = id ? parseInt(id) : 0;
+ const eventId = id && !isNaN(Number(id)) ? parseInt(id) : 0;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 329c80a and 1cdc7b9.

📒 Files selected for processing (5)
  • src/features/dashboard/hook/useEmailHook.ts (1 hunks)
  • src/pages/dashboard/ui/ParticipantsMangementPage.tsx (1 hunks)
  • src/pages/dashboard/ui/mail/EmailEditPage.tsx (1 hunks)
  • src/pages/dashboard/ui/mail/EmailPage.tsx (1 hunks)
  • src/widgets/dashboard/ui/EmailModal.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/pages/dashboard/ui/ParticipantsMangementPage.tsx
  • src/pages/dashboard/ui/mail/EmailEditPage.tsx
  • src/features/dashboard/hook/useEmailHook.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/widgets/dashboard/ui/EmailModal.tsx (3)
src/features/dashboard/api/mail.ts (1)
  • sendEmail (17-20)
src/features/dashboard/hook/useEmailHook.ts (1)
  • useSendEmail (15-30)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (19-41)
🪛 ESLint
src/pages/dashboard/ui/mail/EmailPage.tsx

[error] 63-63: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: storybook
🔇 Additional comments (5)
src/pages/dashboard/ui/mail/EmailPage.tsx (2)

7-10: React Router와 상태 관리 관련 임포트가 잘 추가되었습니다.

기존 useNavigate 대신 useParams를 사용하여 URL 파라미터를 가져오는 방식으로 변경되었고, 이메일 상태 관리를 위한 Zustand 스토어와 이메일 전송 API 연동을 위한 커스텀 훅이 적절히 임포트되었습니다.


66-69: TimePicker 컴포넌트 연결이 적절하게 구현되었습니다.

상태 관리 스토어의 함수를 활용하여 시간과 날짜 선택 이벤트를 처리하는 방식으로 잘 구현되었습니다.

src/widgets/dashboard/ui/EmailModal.tsx (3)

1-5: 적절한 의존성과 상태 관리 구조로 리팩토링되었습니다.

이메일 모달이 Zustand 스토어 기반 상태 관리로 마이그레이션되어 더 나은 구조를 갖추게 되었습니다. React Router와 API 훅을 적절히 통합하여 일관된 아키텍처를 구현했습니다.


14-25: 전역 상태 관리로 적절히 마이그레이션 되었습니다.

useParams와 useEmailStore를 사용하여 컴포넌트 내 로컬 상태에서 중앙 집중식 상태 관리로 마이그레이션한 것은 좋은 접근 방식입니다. 이를 통해 상태 관리가 간소화되고 다른 컴포넌트와의 일관성이 유지됩니다.


64-67: TimePicker 컴포넌트가 스토어와 잘 통합되었습니다.

TimePicker의 콜백 함수가 Zustand 스토어의 상태를 직접 업데이트하도록 설정되어 있어, 상태 관리가 일관되게 유지됩니다.

@github-actions
Copy link
Copy Markdown

github-actions bot commented May 3, 2025

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/features/dashboard/hook/useEmailHook.ts (2)

59-71: 🛠️ Refactor suggestion

알림 UI 개선 필요

이전 리뷰에서 제안된 대로 alert() 대신 toast 알림을 사용하는 것이 사용자 경험에 더 좋습니다. queryClient.invalidateQueries는 적용되었지만, 알림 메시지는 여전히 alert를 사용하고 있습니다.

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { sendEmail, editEmail, readEmail, deleteEmail, readPurchaserEmails } from '../api/mail';
import { EmailRequest, EmailResponse, ReadEmailResponse } from '../model/emailInformation';
import { useNavigate, useParams } from 'react-router-dom';
import { useEmailStore } from '../model/EmailStore';
+import { toast } from 'react-toastify';

// ... 중략 ...

export const useDeleteEmail = () => {
    const queryClient = useQueryClient();
    return useMutation<EmailResponse, Error, number>({
        mutationFn: (reservationEmailId) => deleteEmail(reservationEmailId),
        onSuccess: () => {
-           alert("메일이 삭제되었습니다.");
+           toast.success("메일이 삭제되었습니다.");
            queryClient.invalidateQueries({ queryKey: ['emails'] });
        },
        onError: () => {
-           alert("메일 삭제에 실패했습니다. 다시 시도해 주세요.");
+           toast.error("메일 삭제에 실패했습니다. 다시 시도해 주세요.");
        }
    });
}

30-40: 🛠️ Refactor suggestion

알림 UI 개선 필요

사용자 경험 향상을 위해 alert() 대신 toast 알림을 사용하는 것이 더 좋습니다. 알림은 페이지 흐름을 방해하지 않고 자연스럽게 표시되어야 합니다.

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { sendEmail, editEmail, readEmail, deleteEmail, readPurchaserEmails } from '../api/mail';
import { EmailRequest, EmailResponse, ReadEmailResponse } from '../model/emailInformation';
import { useNavigate, useParams } from 'react-router-dom';
import { useEmailStore } from '../model/EmailStore';
+import { toast } from 'react-toastify';

// ... 중략 ...

export const useSendEmail = () => {
    const { id } = useParams();
    const navigate = useNavigate();
    const { reset } = useEmailStore();
    return useMutation<EmailResponse, Error, EmailRequest>({
        mutationFn: sendEmail,
        onSuccess: () => {
            reset();
-           alert("예약 메일이 성공적으로 발송되었습니다!");
+           toast.success("예약 메일이 성공적으로 발송되었습니다!");
            navigate(`/dashboard/${id}/mailBox`);
        },
        onError: () => {
-           alert("메일 전송에 실패했습니다. 다시 시도해 주세요.");
+           toast.error("메일 전송에 실패했습니다. 다시 시도해 주세요.");
        },
    });
};
🧹 Nitpick comments (3)
src/widgets/dashboard/ui/SelectTicketInfo.tsx (1)

10-10: 코드 가독성 개선이 필요합니다.

콤마 뒤에 공백이 없어 가독성이 떨어집니다.

-const SelectTicketInfo = ({ tickets,onClick}: SelectTicketInfoProps) => {
+const SelectTicketInfo = ({ tickets, onClick }: SelectTicketInfoProps) => {
src/features/dashboard/api/mail.ts (1)

31-34: API 에러 처리 개선 필요

현재 함수는 API 호출 성공 시에만 처리하고 있습니다. API 호출 실패 시 에러를 적절히 처리하는 로직을 추가하는 것이 좋습니다.

export const sendEmail = async (data: EmailRequest) => {
+   try {
        const response = await axiosClient.post('/reservation-emails', data);
        return response.data.result;
+   } catch (error) {
+       console.error('Failed to send email:', error);
+       throw error;
+   }
};
src/features/dashboard/hook/useEmailHook.ts (1)

15-23: 코드 포맷팅 개선 필요

usePurchaserEmails 훅의 들여쓰기가 다른 훅들과 일관되지 않습니다. 코드 가독성을 위해 일관된 포맷팅이 필요합니다.

export const usePurchaserEmails = () => {
    return useMutation({
        mutationFn: ({ eventId, ticketId }: { eventId: number; ticketId?: number }) =>
-          readPurchaserEmails(eventId, ticketId),
+         readPurchaserEmails(eventId, ticketId),
        onError: () => {
            alert('이메일을 불러오는 데 실패했습니다.');
-          },
-      });
-  };
+       },
+   });
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cdc7b9 and b8cf849.

📒 Files selected for processing (11)
  • src/features/dashboard/api/mail.ts (1 hunks)
  • src/features/dashboard/hook/useEmailHook.ts (1 hunks)
  • src/features/dashboard/model/EmailStore.tsx (1 hunks)
  • src/features/dashboard/model/emailInformation.ts (1 hunks)
  • src/features/dashboard/model/participantInformation.ts (1 hunks)
  • src/features/event-manage/event-create/ui/TimePicker.tsx (1 hunks)
  • src/pages/dashboard/ui/ParticipantsMangementPage.tsx (1 hunks)
  • src/pages/dashboard/ui/mail/EmailPage.tsx (1 hunks)
  • src/widgets/dashboard/ui/EmailModal.tsx (2 hunks)
  • src/widgets/dashboard/ui/SelectTicketInfo.tsx (2 hunks)
  • src/widgets/dashboard/ui/SelectTicketModal.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/features/dashboard/model/participantInformation.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/pages/dashboard/ui/ParticipantsMangementPage.tsx
  • src/features/dashboard/model/EmailStore.tsx
  • src/features/dashboard/model/emailInformation.ts
  • src/features/event-manage/event-create/ui/TimePicker.tsx
  • src/pages/dashboard/ui/mail/EmailPage.tsx
  • src/widgets/dashboard/ui/EmailModal.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/features/dashboard/api/mail.ts (2)
src/features/dashboard/model/emailInformation.ts (2)
  • ReadEmailResponse (14-21)
  • EmailRequest (1-7)
src/shared/types/api/http-client.ts (1)
  • axiosClient (6-13)
src/widgets/dashboard/ui/SelectTicketInfo.tsx (1)
src/features/ticket/model/ticketInformation.ts (1)
  • ReadTicketResponse (21-27)
src/features/dashboard/hook/useEmailHook.ts (3)
src/features/dashboard/model/emailInformation.ts (3)
  • ReadEmailResponse (14-21)
  • EmailResponse (8-13)
  • EmailRequest (1-7)
src/features/dashboard/api/mail.ts (5)
  • readEmail (5-16)
  • readPurchaserEmails (19-29)
  • sendEmail (31-34)
  • editEmail (36-39)
  • deleteEmail (41-44)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (17-36)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: storybook

@github-actions
Copy link
Copy Markdown

github-actions bot commented May 3, 2025

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/widgets/dashboard/ui/SelectTicketModal.tsx (2)

14-20: 동적 데이터 페칭과 상태 관리 구현 적절

URL에서 이벤트 ID를 추출하고 이를 기반으로 티켓 정보를 불러오는 방식이 잘 구현되어 있습니다. fallback 처리(data?.result ?? [])도 적절하게 되어 있어 데이터가 없는 경우에도 오류 없이 동작합니다.

다만, 타입 안전성을 더 강화하기 위해 eventId 변수에 명시적인 타입을 추가하는 것을 고려해보세요:

- const eventId = id ? parseInt(id) : 0;
+ const eventId: number = id ? parseInt(id) : 0;

46-58: 로딩 상태 처리와 동적 데이터 렌더링 개선 가능

로딩 상태를 적절히 처리하고 동적 데이터를 기반으로 티켓 목록을 렌더링하는 방식은 좋습니다. 하지만 로딩 UI가 매우 단순합니다.

사용자 경험 향상을 위해 스켈레톤 UI나 스피너 등을 활용하여 로딩 상태를 개선하는 것을 고려해보세요:

{isLoading ? (
-  <div>로딩 중...</div>
+  <div className="flex justify-center items-center py-4">
+    <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
+    <span className="ml-2">티켓 정보를 불러오는 중입니다...</span>
+  </div>
) : (
  tickets.map((ticket) => (
    // ...
  ))
)}

또한 티켓 데이터가 비어있는 경우에 대한 처리도 추가하면 좋을 것 같습니다:

{isLoading ? (
  <div>로딩 중...</div>
) : (
+  tickets.length === 0 ? (
+    <div className="text-center py-4 text-gray-500">티켓 정보가 없습니다.</div>
+  ) : (
    tickets.map((ticket) => (
      <SelectTicketInfo
        key={ticket.ticketId}
        tickets={ticket}
        onClick={() => {
          handleClick(ticket.ticketId);
        }}
      />
    ))
+  )
)}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8cf849 and 4116f3f.

📒 Files selected for processing (2)
  • src/features/dashboard/api/mail.ts (1 hunks)
  • src/widgets/dashboard/ui/SelectTicketModal.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/features/dashboard/api/mail.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/widgets/dashboard/ui/SelectTicketModal.tsx (3)
src/features/ticket/hooks/useTicketHook.ts (1)
  • useTickets (5-11)
src/features/dashboard/model/EmailStore.tsx (1)
  • useEmailStore (17-36)
src/features/dashboard/hook/useEmailHook.ts (1)
  • usePurchaserEmails (15-23)
🔇 Additional comments (3)
src/widgets/dashboard/ui/SelectTicketModal.tsx (3)

1-6: 적절한 임포트 구성으로 API 연동 준비 완료

필요한 모든 훅과 컴포넌트를 임포트하여 동적 데이터 페칭과 상태 관리를 위한 준비가 잘 되어 있습니다. useParams를 사용한 URL 파라미터 추출, useTickets를 통한 티켓 데이터 조회, useEmailStore를 활용한 이메일 상태 관리, 그리고 usePurchaserEmails로 구매자 이메일 정보를 가져오는 구조가 명확합니다.


21-32: 이메일 데이터 조회 및 상태 업데이트 로직 구현 적절

티켓 선택 시 해당 티켓의 구매자 이메일 정보를 조회하고 성공 시 상태를 업데이트하는 로직이 잘 구현되어 있습니다. API 호출, 상태 업데이트, 모달 닫기까지의 흐름이 명확합니다.


39-42: 이메일 모달 열기 로직 적절

이전 리뷰에서 지적된 openEmailModal 함수의 중복 호출 문제가 해결되었습니다. 뒤로 가기 버튼의 onClick 핸들러와 handleClick 함수 내의 호출이 각각 다른 사용자 액션에 대응하여 적절히 구현되어 있습니다.

Copy link
Copy Markdown
Contributor

@Yejiin21 Yejiin21 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!

@hyeeuncho hyeeuncho merged commit 2c735a5 into develop May 6, 2025
2 checks passed
@hyeeuncho hyeeuncho deleted the feat/#110/reservation-email branch May 6, 2025 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔧 Feature 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 예약 메일 API 연동

2 participants