Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/domain/admin/program/ChallengeLecture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { FaTrashCan } from 'react-icons/fa6';
const DEFAULT_LECTURE_COUNT = 3;

/** 강의 정보 등록 섹션을 노출할 챌린지 타입 (추가 타입은 이 배열에만 넣으면 됨) */
const CHALLENGE_TYPES_WITH_LECTURES: ChallengeType[] = ['HR'];
const CHALLENGE_TYPES_WITH_LECTURES: ChallengeType[] = ['HR', 'MARKETING'];

function createEmptyLecture() {
return {
Expand Down
7 changes: 4 additions & 3 deletions src/domain/program/challenge/ChallengeMarketingView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ChallengeIntroEditorContent from '@/domain/program/challenge/challenge-view/ChallengeIntroEditorContent';
import ChallengeRecruitmentInfoSection from '@/domain/program/challenge/challenge-view/ChallengeRecruitmentInfoSection';
import ChallengeTabNavigation from '@/domain/program/challenge/challenge-view/ChallengeTabNavigation';
import ChallengeIntroEditorContent from '@/domain/program/challenge/challenge-view/ChallengeIntroEditorContent';
import MarketingCurriculumSection from '@/domain/program/challenge/marketing-view/MarketingCurriculumSection';
import MarketingDifferentiatorsSection from '@/domain/program/challenge/marketing-view/MarketingDifferentiatorsSection';
import MarketingPricingSection from '@/domain/program/challenge/marketing-view/MarketingPricingSection';
Expand Down Expand Up @@ -33,11 +33,12 @@ const ChallengeMarketingView = ({ challenge }: Props) => {
<ChallengeIntroEditorContent challenge={challenge} />
<MarketingIntroSection />
<MarketingFeaturesSection />
<MarketingDifferentiatorsSection />
<MarketingCurriculumSection />
<MarketingDifferentiatorsSection lectures={content?.lectures} />
<MarketingCurriculumSection content={content} />
<MarketingChallengeCalendar
challenge={challenge}
curriculumImage={content?.curriculumImage}
lectureCount={content?.lectures?.length}
/>
<MarketingSummarySection />
<MarketingApplicationStrategySection />
Expand Down
11 changes: 9 additions & 2 deletions src/domain/program/challenge/hr-view/HrCurriculumCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ReactNode } from 'react';
interface Props {
challenge: ChallengeIdPrimitive;
curriculumImage?: string;
lectureCount?: number;
}

interface CalendarItemProps {
Expand Down Expand Up @@ -53,7 +54,13 @@ const CalendarList = ({ children }: CalendarListProps) => {
);
};

const HrCurriculumCalendar = ({ challenge, curriculumImage }: Props) => {
const DEFAULT_LECTURE_COUNT = 4;

const HrCurriculumCalendar = ({
challenge,
curriculumImage,
lectureCount = DEFAULT_LECTURE_COUNT,
}: Props) => {
const calendarItems = [
{
number: 1,
Expand All @@ -70,7 +77,7 @@ const HrCurriculumCalendar = ({ challenge, curriculumImage }: Props) => {
{
number: 2,
bgColor: 'bg-[#FB923C]',
title: '현직자 LIVE 세미나 4회',
title: `현직자 LIVE 세미나 ${lectureCount}회`,
description: (
<div className="leading-[20px] md:leading-[22px]">
채용, 리크루팅, HRD, People Analytics 등 <br />
Expand Down
26 changes: 10 additions & 16 deletions src/domain/program/challenge/hr-view/HrCurriculumContent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import dayjs from '@/lib/dayjs';
import { twMerge } from '@/lib/twMerge';
import { ChallengeCurriculum } from '@/types/interface';
import Image from 'next/image';
import { ReactNode } from 'react';

interface WeekGroup {
Expand Down Expand Up @@ -63,26 +62,21 @@ function Highlight({
className,
)}
>
<div className="flex min-w-0 items-center gap-1.5">
<p
className={twMerge(
textStyle,
'min-w-0 whitespace-pre-line break-words',
)}
>
{contentImg && (
<Image
<img
src={contentImg}
alt=""
unoptimized
width={28}
height={28}
className="h-5 w-5 flex-shrink-0 md:h-7 md:w-7"
className="mb-1 mr-2 inline-block h-5 align-top md:h-7"
/>
)}
<p
className={twMerge(
textStyle,
'min-w-0 flex-1 whitespace-pre-line break-words',
)}
>
{description}
</p>
</div>
{description}
</p>
</div>
);
}
Expand Down
76 changes: 41 additions & 35 deletions src/domain/program/challenge/hr-view/HrCurriculumPointsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,45 @@ interface HrCurriculumPointsSectionProps {
content: ChallengeContent | null;
}

const curriculumCards = [
{
title: 'HR 실무 역량 Class',
description: (
<>
HR 직무에서 수행해야 하는 사전 과제,
<br />
HR 관련 뉴스기사, 아티클 등 수집하는
<br />
스터디까지 지원해드려요
</>
),
},
{
title: '현직자의 LIVE 세미나',
description: (
<>
4명의 HR 현직자 선배들이
<br />
어떻게 HR 커리어를 시작했는지,
<br />그 이야기를 직접 들려드릴게요
</>
),
},
{
title: '챌린지를 통한 서류 완성',
description: (
<>
채용 공고에 바로 지원이 가능하도록,
<br />
수준급의 서류를 무조건 완성해요
</>
),
},
];
const DEFAULT_LECTURE_COUNT = 5;

function getCurriculumCards(lectureCount: number) {
return [
{
title: 'HR 실무 역량 Class',
description: (
<>
HR 직무에서 수행해야 하는 사전 과제,
<br />
HR 관련 뉴스기사, 아티클 등 수집하는
<br />
스터디까지 지원해드려요
</>
),
},
{
title: '현직자의 LIVE 세미나',
description: (
<>
{lectureCount}명의 HR 현직자 선배들이
<br />
어떻게 HR 커리어를 시작했는지,
<br />그 이야기를 직접 들려드릴게요
</>
),
},
{
title: '챌린지를 통한 서류 완성',
description: (
<>
채용 공고에 바로 지원이 가능하도록,
<br />
수준급의 서류를 무조건 완성해요
</>
),
},
];
}

const CurriculumCard = ({
title,
Expand Down Expand Up @@ -80,6 +84,8 @@ const HrCurriculumPointsSection: React.FC<HrCurriculumPointsSectionProps> = ({
content,
}) => {
const weekText = content?.challengePoint?.weekText ?? '3주';
const lectureCount = content?.lectures?.length ?? DEFAULT_LECTURE_COUNT;
const curriculumCards = getCurriculumCards(lectureCount);

return (
<section className="flex w-full flex-col items-center pb-[70px] pt-[50px] text-center md:overflow-x-hidden md:pb-[82px] md:pt-[141px]">
Expand Down
16 changes: 6 additions & 10 deletions src/domain/program/challenge/hr-view/HrCurriculumSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ const HrCurriculumSection: React.FC<HrCurriculumSectionProps> = ({
challenge,
content,
}) => {
if (
!content ||
!content.curriculum ||
content.curriculum.length === 0
) {
if (!content || !content.curriculum || content.curriculum.length === 0) {
return null;
}

const weekText = content?.challengePoint?.weekText ?? '3주';
const lectureCount = content?.lectures?.length ?? 5;

return (
<section
Expand All @@ -34,21 +31,20 @@ const HrCurriculumSection: React.FC<HrCurriculumSectionProps> = ({
</SectionHeader>
<MainTitle className="flex flex-col items-center gap-1">
<span>
6회의 미션 <br className="md:hidden" />+ 현직자 LIVE 세미나 4회와 함께
6회의 미션 <br className="md:hidden" />+ 현직자 LIVE 세미나{' '}
{lectureCount}회와 함께
</span>
<span>만드는 밀도 있는 {weekText}간의 여정</span>
</MainTitle>
<div className="text-small14 mb-[60px] mt-3 text-center text-neutral-0 md:mt-5 md:text-small18">
막연한 HR 관심에서 끝나지 않도록, 직무 탐색부터 경험 정리, 결과물
완성까지 함께합니다.
</div>
<HrCurriculums
curriculum={content.curriculum}
content={content}
/>
<HrCurriculums curriculum={content.curriculum} content={content} />
<HrCurriculumCalendar
challenge={challenge}
curriculumImage={content.curriculumImage}
lectureCount={lectureCount}
/>
</section>
);
Expand Down
79 changes: 51 additions & 28 deletions src/domain/program/challenge/hr-view/HrCurriculums.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,51 @@ const SidebarButton = ({
);
};

interface DesktopHrCurriculumsProps {
groupedByWeek: WeekGroup[];
sidebarList: {
date: string;
title: string;
}[];
}

function DesktopHrCurriculums({
groupedByWeek,
sidebarList,
}: DesktopHrCurriculumsProps) {
const [active, setActive] = useState(0);

const safeActiveIndex =
active >= 0 && active < groupedByWeek.length ? active : 0;
const activeGroup = groupedByWeek[safeActiveIndex];

if (!activeGroup) {
return null;
}

return (
<div className="hidden w-full max-w-[1262px] items-stretch overflow-hidden rounded-sm bg-white md:flex">
{/* Sidebar */}
<div className="flex min-w-fit max-w-[413px] flex-1 shrink-0 flex-col gap-[10px] border-r border-neutral-80 px-8 py-[30px]">
{sidebarList.map((item, index) => (
<SidebarButton
key={`sidebar-button-${index}`}
index={index}
date={item.date}
title={item.title}
active={index === safeActiveIndex}
onClick={() => setActive(index)}
/>
))}
</div>
{/* Content */}
<div className="h-[634px] min-w-0 flex-1 shrink-0 flex-col gap-3 overflow-y-auto overflow-x-hidden px-8 pb-11 pt-10">
<HrCurriculumContent weekGroup={activeGroup} />
</div>
</div>
);
}

function HrCurriculums({ curriculum, content }: HrCurriculumsProps) {
const isMobile = useMediaQuery('(max-width:768px)');

Expand Down Expand Up @@ -162,33 +207,6 @@ function HrCurriculums({ curriculum, content }: HrCurriculumsProps) {
title: group.weekTitle,
}));

const ContentWithSidebar = () => {
const [active, setActive] = useState(0);
const activeGroup = groupedByWeek[active];

return (
<div className="hidden w-full max-w-[1262px] items-stretch overflow-hidden rounded-sm bg-white md:flex">
{/* Sidebar */}
<div className="flex min-w-fit max-w-[413px] flex-1 shrink-0 flex-col gap-[10px] border-r border-neutral-80 px-8 py-[30px]">
{sidebarList.map((item, index) => (
<SidebarButton
key={`sidebar-button-${index}`}
index={index}
date={item.date}
title={item.title}
active={index === active}
onClick={() => setActive(index)}
/>
))}
</div>
{/* Content */}
<div className="h-[634px] min-w-0 flex-1 shrink-0 flex-col gap-3 overflow-y-auto overflow-x-hidden px-8 pb-11 pt-10">
<HrCurriculumContent weekGroup={activeGroup} />
</div>
</div>
);
};

if (isMobile) {
return (
<div className="flex w-full min-w-[320px] max-w-full flex-col items-stretch gap-3 overflow-x-hidden md:hidden">
Expand All @@ -212,7 +230,12 @@ function HrCurriculums({ curriculum, content }: HrCurriculumsProps) {
);
}

return <ContentWithSidebar />;
return (
<DesktopHrCurriculums
groupedByWeek={groupedByWeek}
sidebarList={sidebarList}
/>
);
}

export default HrCurriculums;
Loading