Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] 클래스 카드 공통 컴포넌트 제작 #87

Merged
merged 12 commits into from
Jan 16, 2025

Conversation

heesunee
Copy link
Collaborator

@heesunee heesunee commented Jan 16, 2025

📌 Related Issues

✅ 체크 리스트

  • PR 제목의 형식을 잘 작성했나요? e.g. [Feat] PR 템플릿 작성
  • 빌드가 성공했나요? (pnpm build)
  • 컨벤션을 지켰나요?
  • 이슈는 등록했나요?
  • 리뷰어와 라벨을 지정했나요?

📄 Tasks

  • 마이페이지 내 클래스 예약 목록 페이지에서 사용되는 카드 컴포넌트, 강사가 개설한 클래스를 확인하는 페이지에서 사용되는 카드 컴포넌트를 공통 컴포넌트로 묶어서 제작했습니다.

⭐ PR Point (To Reviewer)

  • 대부분의 페이지에서 사용되는 컴포넌트가 아니라, 특정 페이지 여러부분에서 공유되는 컴포넌트라 위치를 고민했었는데, 그래도 다른 페이지에서도 사용 가능한 컴포넌트라면 common component로 하는 것이 맞다고 생각해서 공통 컴포넌트 폴더에 배치했습니다.
  • 미묘하게 글자나 버튼의 유무가 다른 컴포넌트라 어느정도 확장성을 고려해서 작성했는데, 추가로 분리했으면 하는 부분이나, 삼항 연산자를 조금 지저분하게 사용한 부분도 있는 것 같아서 더 나은 개선안이 있다면 피드백 많이 부탁드립니다!.
  1. `timeCalculate.ts
export type ClassStatus = 'ongoing' | 'upcoming' | 'completed';

// 레슨 시작, 끝 시간과 현재 시간을 비교해서 클래스/레슨의 상태를 계산하는 함수
export const getClassStatus = (
  lessonStartDateTime: string,
  lessonEndDateTime: string
): { status: ClassStatus; remainingDays?: number } => {
  const currentTime = new Date();
  const startTime = new Date(lessonStartDateTime);
  const endTime = new Date(lessonEndDateTime);

  if (currentTime < startTime) {
    // 수강 예정 상태
    const remainingDays = Math.ceil((startTime.getTime() - currentTime.getTime()) / (1000 * 60 * 60 * 24));
    return { status: 'upcoming', remainingDays };
  }

  if (currentTime >= startTime && currentTime <= endTime) {
    // 수강 중 상태
    return { status: 'ongoing' };
  }

  // 수강 완료 상태
  return { status: 'completed' };
};
  • 클래스 카드 컴포넌트 상단의 현재 클래스의 상태를 계산하는 함수입니다. 현재 시간과 레슨의 시작과 끝시간을 이용해서 수강이 완료된 상태인지 (completed), 수강 중 상태인지 (ongoing), 수강예정 상태(upcominc)인지 반환합니다.
  • 수강 예정 상태의 경우에는 D-N일의 형태로 남은 일수를 계산해야 하기 때문에 시작 시간에서 현재시간을 빼고 변형해 남은 일자를 remainigDays에 저장했습니다.
  • 강사가 개설한 클래스의 경우, 내가 개설한 시점부터지 모집 예정의 시점은 존재하지 않기 때문에 ongoing 또는completed 두 가지의 경우만 갖게 됩니다.
  1. isReservation
interface classCardProps {
  lessonId?: string;
  reservationId?: string;
  lessonName: string;
  lessonImageUrl: string;
  lessonGenre: string;
  lessonLevel: string;
  lessonLocation: string;
  lessonStartDateTime: string;
  lessonEndDateTime: string;
  isReservation?: boolean;
  children?: React.ReactNode;
}
  • 클래스 예약 내역 페이지에서는 (수강 예정, 수강중, 수강완료)의 상태와 문의하기 > 버튼이 있고, 강사가 개설한 클래스 관리 페이지에서는 (모집중, 모집완료)의 상태와 문의하기 버튼이 없기 때문에 둘을 구분하기 위해 isReservtion이라는 prop을 추가했습니다.
  • ClassCard 컴포넌트 내부에서는 isReservation의 true, false에 따라 다른 디자인을 렌더링합니다.
   <Flex justify="spaceBetween" align="center">
        <Flex align="center" gap="0.2rem" marginBottom="1.2rem">
          <Text tag="b4" color={status === 'completed' ? 'gray8' : 'black'}>
            {isReservation
              ? status === 'upcoming'
                ? '수강예정'
                : status === 'ongoing'
                  ? '수강중'
                  : '수강완료'
              : status === 'upcoming'
                ? '모집중'
                : '모집완료'}
          </Text>
          {isReservation && status === 'upcoming' && remainingDays !== undefined && (
            <Text tag="b7" color="main4">
              D-{remainingDays}
            </Text>
          )}
        </Flex>

        {isReservation && (
          <Flex align="center" gap="0.2rem">
            <Text tag="b7" color="gray7">
              문의하기
            </Text>
            <IcArrowRightGray0614 width="1.4rem" height="1.4rem" />
          </Flex>
        )}
      </Flex>
  • 뭔가 더 효율적인 로직이 있을 것 같기도 한데.. 우선 반환되는 status 값과 isReservation의 값에 따라 조건부 렌더링 되게 작성해놓았습니다.
  1. children으로 BoxButton 처리
  • 전체적인 디자인은 모두 비슷하지만, 카드 컴포넌트 하단 버튼의 유무, 버튼에 들어가는 글자가 미세하게 다르기 때문에 우선 하단 부분에 children을 놓았습니다.
{children && <Flex gap="0.7rem">{children}</Flex>}

📷 Screenshot

  • 스토리북으로 확인 가능합니다.
    <기본, children 없음>
    image

<children 있음>
image

<lesson 카드일때>
image

<수강예정 카드일때>
image

🔔 ETC

@heesunee heesunee added 🛠️ Feature 기능 개발 희선 자중해 labels Jan 16, 2025
@heesunee heesunee self-assigned this Jan 16, 2025
Copy link
Collaborator

@constantly-dev constantly-dev left a comment

Choose a reason for hiding this comment

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

수고하셨습니다! 공통 컴포넌트로 두는 것에 대해 고민하신 것도 정말 잘 하신 것 같고 분기 처리도 상황에 맞게 잘 하신 것 같아요! 삼항 연산자 중첩 관련은 단순히 가독성 측면에서 참고 정도로 말씀 드린 거라 한번 더 생각해보시면 좋을 것 같습니다 수고하셨어요~

Comment on lines 41 to 51
<Text tag="b4" color={status === 'completed' ? 'gray8' : 'black'}>
{isReservation
? status === 'upcoming'
? '수강예정'
: status === 'ongoing'
? '수강중'
: '수강완료'
: status === 'upcoming'
? '모집중'
: '모집완료'}
</Text>
Copy link
Collaborator

Choose a reason for hiding this comment

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

여러 분기가 되는 컴포넌트라 여러 상황을 생각해서 설계 했어야 할텐데 수고하셨습니다!
다만 삼항 연산자의 중첩 사용은 가독성이 좋지 않아서 많아진다면 if문 혹은 switch문을 사용하시는 것도 좋은 방법이 될 것 같습니다!

switch문 예시

let text;

if (isReservation) {
  switch (status) {
    case 'upcoming':
      text = '수강예정';
      break;
    case 'ongoing':
      text = '수강중';
      break;
    case 'completed':
      text = '수강완료';
      break;
    default:
      text = '';
  }
} else {
  switch (status) {
    case 'upcoming':
      text = '모집중';
      break;
    case 'completed':
      text = '모집완료';
      break;
    default:
      text = '';
  }
}

<Text tag="b4" color={status === 'completed' ? 'gray8' : 'black'}>
  {text}
</Text>

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

반영완~

Comment on lines 10 to 22
interface classCardProps {
lessonId?: string;
reservationId?: string;
lessonName: string;
lessonImageUrl: string;
lessonGenre: string;
lessonLevel: string;
lessonLocation: string;
lessonStartDateTime: string;
lessonEndDateTime: string;
isReservation?: boolean;
children?: React.ReactNode;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

interface는 PascalCase를 사용해주세요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

반영 완~

Copy link
Member

@KIMGEONHWI KIMGEONHWI left a comment

Choose a reason for hiding this comment

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

고생하셨습니다! 뚝딱뚝딱 잘만드시네요👍

Comment on lines 10 to 22
interface classCardProps {
lessonId?: string;
reservationId?: string;
lessonName: string;
lessonImageUrl: string;
lessonGenre: string;
lessonLevel: string;
lessonLocation: string;
lessonStartDateTime: string;
lessonEndDateTime: string;
isReservation?: boolean;
children?: React.ReactNode;
}
Copy link
Member

Choose a reason for hiding this comment

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

Type이 많이 길어진거 같은데 분리해서 사용하는 것은 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

반영했습니다 !

Copy link
Collaborator

@rtttr1 rtttr1 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다! 코멘트 하나 확인해주세요!


if (currentTime < startTime) {
// 수강 예정 상태
const remainingDays = Math.ceil((startTime.getTime() - currentTime.getTime()) / (1000 * 60 * 60 * 24));
Copy link
Collaborator

Choose a reason for hiding this comment

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

지금 날짜와 수강 시작 날짜를 비교할때

currentTime.setHours(0, 0, 0, 0);
startTime.setHours(0, 0, 0, 0);

이렇게 시간,분,초를 0으로 맞추고 비교해줘야지 정확하게 날짜만 계산이 됩니다!
아닌 경우에는 날짜로는 하루가 차이나도 실제 시간은 12시간정도의 차이면 0.xxx 로 계산되어서 정확한 디데이가 안나오는 경우가 있습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

반영했습니다 !!

@heesunee heesunee merged commit 67c2445 into develop Jan 16, 2025
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] 클래스 (예약/관리) 카드 공통 컴포넌트 제작
4 participants