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

[ Feature/dev 58 ] 메인 페이지 > 워크북 카드 컴포넌트 ui #126

Merged
merged 13 commits into from
Jul 31, 2024

Conversation

Happhee
Copy link
Collaborator

@Happhee Happhee commented Jul 28, 2024

🔥 Related Issues

https://linear.app/fewletter/issue/DEV-58/워크북-단일-컴포넌트-구현

💜 작업 내용

  • 워크북 카드 ui컴포넌트 제작
  • 데이터 모델링 구현
  • 데이터 모델링 함수 유닛 테스트

✅ PR Point

workbookCardModel

import {
  WorkbookClientInfo,
  WorkbookServerInfo,
  WorkbookSubscriptionInfo,
} from "@main/types/workbook";

export class WorkbookCardModel {
  constructor({
    initWorkbookSeverList,
    initWorkbookSubscriptionInfoList,
  }: {
    initWorkbookSeverList: WorkbookServerInfo[];
    initWorkbookSubscriptionInfoList?: WorkbookSubscriptionInfo[];
  }) {
    this.workbookList = initWorkbookSeverList;
    if (initWorkbookSubscriptionInfoList)
      this.workbookSubscriptionInfoList = initWorkbookSubscriptionInfoList;
    this.workbookCombineList = this.getWorkbookServerCombineData();
  }
// ✅ 구독정보 유무에 따른 카드 데이터 합치기 -> 배열형태
  get workbookCombineListData() {
    return this.workbookCombineList;
  }
  getWorkbookServerCombineData(): WorkbookCombineInfo[] {
    if (this.workbookSubscriptionInfoList) {
      const workbookCombineSet: WorkbookCombineInfoSet = {};

      const workbookSetList = this.transformDataToSet({
        data: this.workbookList,
      });
      const workbookSetSubscriptionInfoList = this.transformDataToSet({
        data: this.workbookSubscriptionInfoList,
      });

      for (const workbookKey in workbookSetList) {
        const isCommonKey =
          Object.prototype.hasOwnProperty.call(workbookSetList, workbookKey) &&
          Object.prototype.hasOwnProperty.call(
            workbookSetSubscriptionInfoList,
            workbookKey,
          );

        if (isCommonKey) {
          const subscriptionItem = workbookSetSubscriptionInfoList[workbookKey];
          const workbookItem = workbookSetList[workbookKey];

          workbookCombineSet[workbookKey] = {
            ...workbookItem,
            ...subscriptionItem,
          };
        } else {
          const workbookItem = workbookSetList[workbookKey];
          workbookCombineSet[workbookKey] = {
            ...workbookItem,
          };
        }
      }

      return Object.entries(workbookCombineSet).map(([key, value]) => ({
        id: Number(key),
        ...value,
      })) as WorkbookCombineInfo[];
    }
    return this.workbookList;
  }
// ✅합친 데이터로 클라이언트 컴포넌트 데이터 변환
  workbookCardList({
    workbookCombineList,
  }: {
    workbookCombineList: WorkbookCombineInfo[];
  }): WorkbookClientInfo[] {
    return workbookCombineList.map(
      ({
        mainImageUrl,
        title,
        description,
        category,
        writers,
        subscriberCount,
        status,
        currentDay,
        totalDay,
      }) => {
        const changeToClientData: WorkbookClientInfo = {
          mainImageUrl,
          title,
          writers: this.getWriterNameList({ writers }),
          metaComponent: this.getMetaComponent({
            category,
            currentDay,
            totalDay,
          }),
          personCourse: this.getPersonCourse({
            subscriberCount,
            status,
          }),
          buttonTitle: this.getButtonTitle({
            status,
            currentDay,
          }),
        };
        return changeToClientData;
      },
    );
  }

// ✅ 작가 이름 리스트
  getWriterNameList({ writers }: { writers: WorkbookServerInfo["writers"] }) {
    return writers.map(({ name }) => name);
  }
// ✅ 날짜 정보 컴포넌트 변환
  getMetaComponent({
    category,
    totalDay,
    currentDay,
  }: {
    category: WorkbookServerInfo["category"];
    totalDay: WorkbookSubscriptionInfo["totalDay"] | undefined;
    currentDay: WorkbookSubscriptionInfo["currentDay"] | undefined;
  }): WorkbookClientInfo["metaComponent"] {
    if (totalDay && currentDay) {
      if (totalDay === currentDay)
        return (
          <p className="body3-bold text-success">
            Day {currentDay}/{totalDay}
          </p>
        );
      return (
        <p className="text-white">
          <span className="body3-bold">Day {currentDay}</span>
          <span className="body3-medium">/{totalDay}</span>
        </p>
      );
    }
    return <p className="body3-medium text-white">{category}</p>;
  }
// ✅ 구독상태에따른 사람수 데이터
  getPersonCourse({
    subscriberCount,
    status,
  }: {
    subscriberCount: WorkbookServerInfo["subscriberCount"];
    status: WorkbookSubscriptionInfo["status"] | undefined;
  }): WorkbookClientInfo["personCourse"] {
    if (status) {
      if (status === "ACTIVE") return `${subscriberCount}명 학습중`;
      if (status === "DONE") return `총 ${subscriberCount}명`;
    }
    return `${subscriberCount}명 학습중`;
  }
// ✅ 구독상태에 따른 버튼 명
  getButtonTitle({
    status,
    currentDay,
  }: {
    status: WorkbookSubscriptionInfo["status"] | undefined;
    currentDay: WorkbookSubscriptionInfo["currentDay"] | undefined;
  }): WorkbookClientInfo["buttonTitle"] {
    if (status && currentDay) {
      if (status === "ACTIVE") return `Day ${currentDay} 학습하기`;
      if (status === "DONE") return "공유하기";
    }
    return "구독하기";
  }
// ✅ 구독 +.워크북리스트 데이터 합칠때, id를 key값으로 가지는 set 데이터로 변환하는 함수
  transformDataToSet({
    data,
  }: {
    data: WorkbookServerInfo[] | WorkbookSubscriptionInfo[];
  }) {
    return data.reduce<WorkbookCombineInfoSet>((acc, item) => {
      const { id, ...rest } = item;
      acc[id] = {
        ...rest,
      };
      return acc;
    }, {});
  }

  private workbookList: WorkbookServerInfo[];
  private workbookSubscriptionInfoList: WorkbookSubscriptionInfo[] | undefined;
  private workbookCombineList: WorkbookCombineInfo[];
}

type WorkbookCombineInfo = WorkbookServerInfo &
  Partial<WorkbookSubscriptionInfo>;
type WorkbookCombineInfoSet = {
  [key: number]:
    | Omit<WorkbookServerInfo, "id">
    | Omit<Partial<WorkbookSubscriptionInfo>, "id">;
};

workbookCardDetail

  • 워크북 카드 안에 있는 컴포넌트는 재사용을 하지 않을것 같아 compound pattern으로 만들어 한곳에서 관리하도록 했습니다.
import { WorkbookClientInfo } from "@main/types/workbook";
import { Button } from "@shared/components/ui/button";
import { cn } from "@shared/utils/cn";
import Image from "next/image";
import FewLogo from "public/assets/icon/cardFewLogo.svg";

const MainImage = ({
  mainImageUrl,
}: Pick<WorkbookClientInfo, "mainImageUrl">) => (
  <Image
    width={269}
    height={172}
    src={mainImageUrl}
    alt=""
    priority
    className="h-[172px] w-[269px] rounded-t-lg object-cover"
  />
);
const WorkbookDetailInfoWrapper = ({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) => (
  <article
    className={cn(
      "flex flex-col",
      "rounded-b-lg bg-black",
      "px-[21px] pb-[25px] pt-[23px]",
    )}
  >
    {children}
  </article>
);

const Title = ({ title }: Pick<WorkbookClientInfo, "title">) => (
  <p className="body3-bold w-auto truncate py-[2px] text-white">{title}</p>
);

const WriterList = ({ writers }: Pick<WorkbookClientInfo, "writers">) => (
  <ul className="sub3-medium flex gap-1 pb-[10px] text-text-gray2">
    {writers.map((writer, idx) => (
      <li key={`workbook-writer-${idx}`}>{writer}</li>
    ))}
  </ul>
);

const PersonCourseWithFewLogo = ({
  personCourse,
}: Pick<WorkbookClientInfo, "personCourse">) => (
  <div className="flex justify-between pb-[26px] pt-[10px]">
    <span className="sub3-medium text-text-gray3">{personCourse}</span>
    <FewLogo width={20} height={20} fill="#264932" />
  </div>
);

const BottomButton = ({
  buttonTitle,
}: Pick<WorkbookClientInfo, "buttonTitle">) => (
  <Button
    className={cn(
      "sub3-semibold bg-white text-black",
      "h-fit rounded py-[4.5px]",
    )}
  >
    {buttonTitle}
  </Button>
);
const WorkbookCardDetail = {
  MainImage,
  WorkbookDetailInfoWrapper,
  Title,
  WriterList,
  PersonCourseWithFewLogo,
  BottomButton,
};

export default WorkbookCardDetail;

😡 Trouble Shooting

  • mock api 연결 진행하면서 test code 구현하도록 하겠습니다...! (따로 pr작성예정)

👀 스크린샷 / GIF / 링크

image

Copy link
Collaborator

Choose a reason for hiding this comment

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

compound pattern 으로 구현했군용!! 좋습니다 👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

이게 서버에서 내려준 API 를 여러 개 결합해야 해서 그 내용을 모델로 구현한 걸로 이해하면 될까용?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@soomin9106 네! 맞습니다. 추가로 카드에 보여질 데이터를 가공하는 부분도 함께만들어서 최대한 card component의 구조를 변경하지 않게 설계했습니다.

@soomin9106
Copy link
Collaborator

확인 했습니닷!! 🥇

@Happhee Happhee merged commit 3c0b9b5 into feature/DEV-8 Jul 31, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants