Skip to content
Merged
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ProfileSettings } from './components/mypage/profile-settings/ProfileSet
import IdeaAnalysis from './components/mypage/idea-analysis/IdeaAnalysis'
import OngoingProject from './components/mypage/ongoing-project/OngoingProject'
import ProfileAnalysis from './components/mypage/profile-analysis/ProfileAnalysis'
import { MatchingStatus } from './components/mypage/matching-status/MatchingStatus'
import IdeaAnalyzePage from './pages/IdeaAnalyzePage'
import AnalyzeReportPage from './pages/AnalyzeReportPage'
import RecruitingProjectsPage from './pages/RecruitingProjectsPage'
Expand Down Expand Up @@ -95,6 +96,10 @@ const router = createBrowserRouter([
path: 'ongoing',
element: <OngoingProject />,
},
{
path: 'matching',
element: <MatchingStatus />,
},
],
},
{
Expand Down
20 changes: 20 additions & 0 deletions src/assets/icons/mypage/chat.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 38 additions & 17 deletions src/components/common/CTAModal.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import Button from './Button'

interface ICTAModal {
interface CTAModalProps {
message: string
subMessage?: string
isMessageHighlight?: boolean
fixedHeight?: boolean
// 단일 버튼 모드
buttonMsg?: string
onButtonClick?: () => void
// 이중 버튼 모드
leftButtonMsg?: string
rightButtonMsg?: string
onLeftClick?: () => void
Expand All @@ -16,11 +20,13 @@ const CTAModal = ({
subMessage = '',
isMessageHighlight = false,
fixedHeight = false,
buttonMsg,
onButtonClick,
leftButtonMsg,
rightButtonMsg,
onLeftClick,
onRightClick,
}: ICTAModal) => {
}: CTAModalProps) => {
// {텍스트} 형식 부분만 보라색으로 하이라이팅
const parseMessage = (text: string) => {
const parts = text.split(/(\{[^}]+\})/g)
Expand All @@ -37,34 +43,49 @@ const CTAModal = ({
})
}

// 단일 버튼 모드인지 확인
const isSingleButtonMode = !!buttonMsg && !leftButtonMsg && !rightButtonMsg

return (
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/50'>
<div
className={`bg-neutral-000 border border-neutral-200 flex flex-col items-center justify-center gap-11 py-14 w-120 rounded-12 ${fixedHeight ? 'h-71.5' : 'h-fit'}`}
className={`bg-neutral-000 border border-neutral-200 flex flex-col items-center justify-center gap-[36px] py-14 w-120 rounded-12 shadow-[0px_6px_20px_0px_#e4e4e4] h-[286px]`}
>
{/* 타이틀 + 설명글 */}
<div
className={`text-center whitespace-pre-line ${isMessageHighlight ? 'text-primary-500-normal' : 'text-neutral-900'} ${fixedHeight ? ' h-40.5' : ''}`}
className={`flex flex-col items-center text-center whitespace-pre-line ${isSingleButtonMode ? 'gap-[18px] py-1' : ''} ${isMessageHighlight ? 'text-primary-500-normal' : 'text-neutral-900'} ${fixedHeight ? ' h-40.5' : ''}`}
>
<span className='leading-[160%] text-[20px] font-bold'>{parseMessage(message)}</span>

{subMessage ? <div className='mt-4.5 body-1 font-normal text-neutral-600'>{subMessage}</div> : ''}
<div className={`body-1 font-medium text-neutral-600 ${isSingleButtonMode ? '' : 'mt-4.5'} ${!subMessage ? 'min-h-[24px]' : ''}`}>
{subMessage}
</div>
</div>
Comment on lines 55 to 63

Choose a reason for hiding this comment

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

medium

이 컴포넌트 내에서 조건부 클래스명을 문자열 템플릿으로 처리하고 있습니다. 프로젝트의 다른 컴포넌트들과 일관성을 맞추고 가독성을 높이기 위해 cn 유틸리티를 사용하는 것이 좋습니다.

먼저 import { cn } from '@/utils/cn'를 추가한 후, 다음과 같이 수정할 수 있습니다.

				<div
					className={cn(
						'flex flex-col items-center text-center whitespace-pre-line',
						isSingleButtonMode && 'gap-[18px] py-1',
						isMessageHighlight ? 'text-primary-500-normal' : 'text-neutral-900',
						fixedHeight && 'h-40.5'
					)}
				>
					<span className='leading-[160%] text-[20px] font-bold'>{parseMessage(message)}</span>

					<div className={cn('body-1 font-medium text-neutral-600', !isSingleButtonMode && 'mt-4.5', !subMessage && 'min-h-[24px]')}>
						{subMessage}
					</div>
				</div>


{/* 선택용 버튼 2개 */}
<div className='flex gap-3 px-7'>
{leftButtonMsg && (
<Button color='mypage1' size='modal' className='min-w-40 w-fit' onClick={onLeftClick}>
{leftButtonMsg}
{/* 버튼 영역 */}
{isSingleButtonMode ? (
/* 단일 버튼 모드 */
<div className='flex items-center justify-center'>
<Button color='mypage2' className='w-40 h-12' onClick={onButtonClick}>
{buttonMsg}
</Button>
)}
</div>
) : (
/* 이중 버튼 모드 */
<div className='flex gap-3 px-7'>
{leftButtonMsg && (
<Button color='mypage1' className='min-w-40 w-fit' onClick={onLeftClick}>
{leftButtonMsg}
</Button>
)}

{rightButtonMsg && (
<Button color='mypage2' size='modal' className='min-w-40 max-w-40' onClick={onRightClick}>
{rightButtonMsg}
</Button>
)}
</div>
{rightButtonMsg && (
<Button color='mypage2' size='modal' className='min-w-40 max-w-40' onClick={onRightClick}>
{rightButtonMsg}
</Button>
)}
</div>
)}
</div>
</div>
)
Expand Down
92 changes: 0 additions & 92 deletions src/components/common/ConfirmModal.tsx

This file was deleted.

42 changes: 42 additions & 0 deletions src/components/mypage/SegmentTabButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { cn } from '@/utils/cn'

interface SegmentTabButtonProps {
/** 탭 텍스트 */
label: string
/** 카운트 (Count 타입일 때 표시) */
count?: number
/** 활성화 여부 */
isActive: boolean
/** 클릭 핸들러 */
onClick?: () => void
/** 추가 클래스명 */
className?: string
}

const SegmentTabButton = ({ label, count, isActive, onClick, className }: SegmentTabButtonProps) => {
return (
<button
type='button'
onClick={onClick}
className={cn('flex flex-col gap-3 w-30 pt-2.5', className)}
>
<div
className={cn(
'title-3 font-semibold text-center flex items-center justify-center gap-1.5',
isActive ? 'text-primary-500-normal' : 'text-neutral-400'
)}
>
<span>{label}</span>
{count !== undefined && <span>{count}</span>}
</div>
<div
className={cn(
'h-0.75 w-full',
isActive ? 'bg-primary-400-normal' : 'bg-neutral-300'
)}
/>
</button>
)
}

export default SegmentTabButton
2 changes: 1 addition & 1 deletion src/components/mypage/idea-analysis/IdeaAnalysis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Section02TeamComposition from './sections/Section02TeamComposition'
import Section03Improvements from './sections/Section03Improvements'
import Section04Roadmap from './sections/Section04Roadmap'
import type { IdeaAnalysisData } from '@/types/mypage/ideaAnalysis'
import CTAModal from '../../common/CTAModal'
import CTAModal from '@/components/common/CTAModal'
import { useNavigate } from 'react-router'
import { useCTAModal } from '@/stores/useCTAModal'

Expand Down
64 changes: 64 additions & 0 deletions src/components/mypage/matching-status/MatchingNotice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { cn } from '@/utils/cn'
import type { NoticeItem } from '@/constants/matchingNotice'

interface MatchingNoticeProps {
/** 유의사항 항목 목록 */
items: NoticeItem[]
/** 추가 클래스명 */
className?: string
}

const MatchingNotice = ({ items, className }: MatchingNoticeProps) => {
return (
<div
className={cn(
'bg-neutral-50 flex flex-col gap-4 items-start px-[22px] py-5 relative rounded-12 w-[784px]',
className
)}
>
{items.map((item, index) => (
<div
key={index}
className={cn(
'flex flex-col items-start relative shrink-0 w-full',
item.subText && 'gap-3'
)}
>
{/* 규칙 메인 텍스트 */}
<p className="title-3 font-semibold text-primary-600-normal h-6 justify-center leading-[1.4] w-full">
<span className="text-neutral-900">{item.number}. </span>
{/* 텍스트 내부의 "/" 처리 */}
{item.text.split(/(\/)/).map((part, partIndex) => {
if (part === '/') {
return <span key={partIndex} className="body-1 font-medium">{part}</span>
}
return <span key={partIndex}>{part}</span>
})}
</p>

{/* 규칙 하위 설명 텍스트 */}
{item.subText && (
<div className="flex flex-col body-1 font-medium text-neutral-700 tracking-[-0.08px] w-full whitespace-pre-wrap">
{item.subText.split('\n').map((line, lineIndex) => (
<p key={lineIndex} className={lineIndex === 0 ? 'mb-0 leading-[1.5]' : 'leading-[1.5]'}>
{line.split(/(\*\*.*?\*\*)/).map((segment, segIndex) => {
if (segment.startsWith('**') && segment.endsWith('**')) {
return (
<span key={segIndex} className="font-bold">
{segment.slice(2, -2)}
</span>
)
}
return <span key={segIndex}>{segment}</span>
})}
</p>
))}
</div>
)}
</div>
))}
</div>
)
}

export default MatchingNotice
Loading