// Modals + My Page const A3 = window.MajorAtoms; const D3 = window.MajorLinkData; const Lc3 = window.lucideReact || {}; const { useState: useS3, useEffect: useE3 } = React; // ============ APPLICANT PROFILE MODAL ============ function ApplicantModal({ applicant, onClose, animKey }) { if (!applicant) return null; const a = applicant; const col = A3.scoreColor(a.score); const [k, setK] = useS3(0); useE3(() => { setK(x => x + 1); }, [a.id]); return (
e.stopPropagation()} className="rounded-3xl bg-white shadow-cardHov overflow-hidden w-full max-w-[640px] max-h-[88vh] overflow-y-auto" style={{ border: '1px solid #E4E4E7' }}>
{a.name}
{a.school}
{col.label}
희망 역할
{a.role}
점수 상세
한 줄 자기소개
"{a.intro}"
주요 기술
{a.skills.map(s => {s})}
포트폴리오 / 링크
{a.portfolio}
이전 프로젝트
{[ { t: '교내 카페 줄서기 IoT 솔루션', r: '프론트엔드', s: 4.6 }, { t: '학생 멘탈 헬스 챗봇', r: '디자인 보조', s: 4.8 }, ].map(p => (
{p.t}
{p.r}
{p.s}
))}
); } // ============ TASK DETAIL MODAL ============ function TaskModal({ task, onClose }) { if (!task) return null; const t = task; const dept = { '컴공/SW': '컴퓨터공학', '디자인': '시각디자인', '경영/기획': '경영학', '미디어/홍보': '미디어커뮤니케이션', '통계/데이터': '통계학' }[t.major] || '컴퓨터공학'; return (
e.stopPropagation()} className="rounded-3xl bg-white shadow-cardHov overflow-hidden w-full max-w-[560px] max-h-[88vh] overflow-y-auto" style={{ border: '1px solid #E4E4E7' }}>
{t.tag && {t.tag}} {t.due}
{t.title}
{t.desc}
담당자
{t.owner}
마감
{t.due}
전공
{t.files > 0 && (
첨부 자료 · {t.files}
{Array.from({ length: Math.min(t.files, 3) }).map((_, i) => (
{['시안_v2.fig', '요약본.pdf', 'screenshot.png', 'notes.md'][i]} {['1.2MB','340KB','2.1MB','8KB'][i]}
))}
)} {t.comments > 0 && (
댓글 · {t.comments}
{[ { who: '박서연', d: '경영학', txt: '히어로 카피 한 줄 더 짧게 갈 수 있을까요?', t: '15분 전' }, { who: '정현우', d: '통계학', txt: '확인했어요, 오늘 안에 PR 올릴게요!', t: '1시간 전' }, ].map((c, i) => (
{c.who}{c.t}
{c.txt}
))}
)}
); } // ============ MY PAGE ============ function ScreenMyPage({ device, animKey }) { const isM = device === 'mobile'; const me = { name: '김민준', dept: '컴퓨터공학', year: 3, school: '한양대', mail: 'mj.kim@hanyang.ac.kr', skills: ['React','Spring','MySQL','Git','Figma'], interests: ['교내 서비스','AI/ML','헬스케어'], trust: { completionRate: 94, dropoutCount: 0, awardSubmissions: [ { id:'awme1', title:'한양 SW 창업 경진대회 대상', projectId:1, proofImageLabel:'상장_한양.jpg', proofLink:'https://hanyang.ac.kr/award', status:'approved' }, { id:'awme2', title:'전국 대학생 해커톤 우수상', projectId:3, proofImageLabel:'hackathon.png', proofLink:'https://hackathon.kr', status:'approved' }, { id:'awme3', title:'캠퍼스 핀테크 공모전 장려상', projectId:1, proofImageLabel:'fintech.jpg', proofLink:'https://fintechcontest.kr', status:'pending' } ], leaderCount: 5, badges: ['리더십','완주왕','마감준수'] } }; return (
{/* Header banner */}
{!isM && } MY PAGE
{me.name}
{me.school}
{me.mail}
{!isM && }
{[ { l: '진행 중', v: 2, c: '#4F46E5' }, { l: '완료', v: 5, c: '#84CC16' }, { l: '평균 ★', v: '4.7', c: '#F59E0B' }, !isM && { l: '평균 매칭', v: '82', c: '#EC4899' }, ].filter(Boolean).map(s => (
{s.l}
{s.v}
))}
전체 보기}>
{D3.PROJECTS.slice(0, 2).map(p => (
window.__nav && window.__nav.go('dashboard', { projectId: p.id })}>
{p.title}
{p.school}·{p.filled}/{p.total}명
D-{p.dday}
))}
{[ { t: '학생 멘탈 헬스 챗봇', r: '프론트엔드', d: '2025.09–12', s: 4.8 }, { t: '교내 카페 줄서기 IoT', r: '프론트엔드 보조', d: '2025.06–08', s: 4.6 }, { t: '광운대 동아리 매칭', r: '풀스택', d: '2024.12–2025.02', s: 4.5 }, ].map((p, i) => (
window.__nav && window.__nav.go('portfolio')}>
0{i+1}
{p.t}
{p.r} · {p.d}
{p.s}
))}
}>
{me.skills.map(s => {s})}
TypeScript 추가하면 매칭 점수가 평균 +9점 올라요
{me.interests.map(s => {s})}
{[ { l: '함께한 사람', v: '14명', c: '#4F46E5' }, { l: '받은 별점', v: '38회', c: '#F59E0B' }, { l: '작성 댓글', v: '127개', c: '#14B8A6' }, { l: '완료 Task', v: '64개', c: '#84CC16' }, ].map(s => (
{s.l} {s.v}
))}
{[ { l: '알림 설정', i: }, { l: '디스코드 연동', i: }, { l: '계정 설정', i: }, { l: '도움말 / 문의', i: }, ].map(r => (
{r.i}
{r.l}
))}
); } // ============ CREATE PROJECT MODAL (wizard) ============ const CP_CATEGORIES = ['교내 서비스', 'AI/ML', 'IoT/하드웨어', '마케팅', '헬스케어', '커머스', '게임/엔터', '기타']; const CP_PERIODS = ['1개월', '2-3개월', '1학기', '기타']; const CP_MEETINGS = ['온라인', '오프라인', '하이브리드']; const CP_ROLES = [ { role: '프론트엔드 개발자', skills: ['React', 'TypeScript'] }, { role: '백엔드 개발자', skills: ['Node.js', 'Spring'] }, { role: '프로덕트 디자이너', skills: ['Figma', 'UX'] }, { role: 'PM/기획자', skills: ['Notion', '리서치'] }, { role: '데이터 분석가', skills: ['Python', 'SQL'] }, { role: '콘텐츠 마케터', skills: ['SNS', '카피'] }, ]; function CreateProjectModal({ onClose }) { const [step, setStep] = useS3(1); const [done, setDone] = useS3(false); const [form, setForm] = useS3({ title: '', desc: '', category: '교내 서비스', period: '2-3개월', total: 5, meeting: '온라인', roles: ['프론트엔드 개발자', '프로덕트 디자이너'], }); const set = (k, v) => setForm(f => ({ ...f, [k]: v })); const toggleRole = (r) => setForm(f => ({ ...f, roles: f.roles.includes(r) ? f.roles.filter(x => x !== r) : [...f.roles, r] })); const canNext = step === 1 ? form.title.trim().length > 0 : step === 2 ? true : form.roles.length > 0; const STEPS = ['기본 정보', '진행 방식', '필요한 역할']; return (
e.stopPropagation()} className="rounded-3xl bg-white shadow-cardHov overflow-hidden w-full max-w-[600px] max-h-[90vh] overflow-y-auto" style={{ border: '1px solid #E4E4E7' }}> {done ? (
프로젝트가 생성됐어요!

"{form.title}" 모집이 시작됐어요. 지원자가 들어오면 매칭 점수와 함께 알려드릴게요.

{form.category} 모집 중
{form.title}
{form.roles.map(r => {r})}
) : ( <>
새 프로젝트 · {step}/3
{STEPS[step - 1]}
{[1, 2, 3].map(s => (
))}
{step === 1 && ( <>
프로젝트 제목 set('title', e.target.value)} autoFocus placeholder="예) 캠퍼스 분실물 찾아주는 앱" className="w-full rounded-xl border px-3.5 py-3 text-[14px] outline-none focus:border-indigo-400" style={{ borderColor: '#E4E4E7' }} />
한 줄 설명