From cd66a4f60dca5f01584a13291af8b3ed58cbef80 Mon Sep 17 00:00:00 2001 From: plumbestie Date: Sun, 17 May 2026 13:29:10 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[Feat]=20=EC=BB=A4=EB=A6=AC=ED=81=98?= =?UTF-8?q?=EB=9F=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20css=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/curriculum/CurriculumPage.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/curriculum/CurriculumPage.module.css b/frontend/src/pages/curriculum/CurriculumPage.module.css index 69b1b67..4f3a711 100644 --- a/frontend/src/pages/curriculum/CurriculumPage.module.css +++ b/frontend/src/pages/curriculum/CurriculumPage.module.css @@ -174,7 +174,7 @@ } .assignmentText { - font-size: 14px; + font-size: 16px; color: var(--gray600); margin: 0; margin-left: 25px; From 884db8e1e0f1e2f3ceec24edee8efeb693c59b5e Mon Sep 17 00:00:00 2001 From: plumbestie Date: Sun, 17 May 2026 14:57:54 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[Feat]=20=EC=BB=A4=EB=A6=AC=ED=81=98?= =?UTF-8?q?=EB=9F=BC=20=EC=83=9D=EC=84=B1=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=BC=88=EB=8C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.js | 5 ++++- .../src/pages/curriculum/CurriculumCreate.js | 17 +++++++++++++++++ .../pages/curriculum/CurriculumPage.module.css | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 frontend/src/pages/curriculum/CurriculumCreate.js diff --git a/frontend/src/App.js b/frontend/src/App.js index f9a9e38..e8903b9 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -5,6 +5,7 @@ import OnboardingPage from './pages/OnboardingPage'; import QnAMainPage from './pages/qna/QnAMainPage'; import QnAListPage from './pages/qna/QnAListPage'; import CurriculumPage from './pages/curriculum/CurriculumPage'; +import CurriculumCreate from './pages/curriculum/CurriculumCreate'; function App() { return ( @@ -20,7 +21,9 @@ function App() { }> } /> } /> - } /> + } /> + {/* 임시 API (role 연결 전이라 create 페이지 일단 따로 만듦) */} + } /> diff --git a/frontend/src/pages/curriculum/CurriculumCreate.js b/frontend/src/pages/curriculum/CurriculumCreate.js new file mode 100644 index 0000000..b832c61 --- /dev/null +++ b/frontend/src/pages/curriculum/CurriculumCreate.js @@ -0,0 +1,17 @@ +import { useState, useEffect } from 'react'; +import styles from './CurriculumPage.module.css'; + +const CurriculumCreate = () => { + const [title, setTitle] = useState("") + + const onChangeTitle = (e) => { + setTitle(e.target.value); + } + return ( +
+ +
+ ); +}; + +export default CurriculumCreate; \ No newline at end of file diff --git a/frontend/src/pages/curriculum/CurriculumPage.module.css b/frontend/src/pages/curriculum/CurriculumPage.module.css index 4f3a711..9eb1097 100644 --- a/frontend/src/pages/curriculum/CurriculumPage.module.css +++ b/frontend/src/pages/curriculum/CurriculumPage.module.css @@ -181,7 +181,7 @@ } .placeholder { - font-size: 13px; + font-size: 16px; color: var(--gray200); text-align: center; padding: 12px 0; From 062f40de25a4150d855b509b9c99e1fd90def84d Mon Sep 17 00:00:00 2001 From: plumbestie Date: Sun, 24 May 2026 17:26:09 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[Feat]=20=EC=9A=B4=EC=98=81=EC=A7=84?= =?UTF-8?q?=EC=9A=A9=20=EC=BB=A4=EB=A6=AC=ED=81=98=EB=9F=BC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.js | 12 +- .../src/pages/curriculum/CurriculumPage.js | 558 +++++++++++------- .../curriculum/CurriculumPage.module.css | 500 ++++++++++++---- 3 files changed, 731 insertions(+), 339 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index ad68149..0e5c892 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -6,7 +6,12 @@ import QnAMainPage from './pages/qna/QnAMainPage'; import QnAListPage from './pages/qna/QnAListPage'; import QnADetailPage from './pages/qna/QnADetailPage'; import CurriculumPage from './pages/curriculum/CurriculumPage'; -import CurriculumCreate from './pages/curriculum/CurriculumCreate'; +import PiroCheckMain from './pages/pirocheck/PIroCheckMain'; +import Attendance from './pages/pirocheck/attendance/Attendance' +import Assignment from './pages/pirocheck/assignment/Assignment'; +import Deposit from './pages/pirocheck/deposit/Deposit'; +import StudentList from './pages/pirocheck/students/StudentList'; +import StudentDetail from './pages/pirocheck/students/StudentDetail'; function App() { return ( @@ -22,9 +27,8 @@ function App() { }> } /> } /> - } /> - {/* 임시 API (role 연결 전이라 create 페이지 일단 따로 만듦) */} - } /> + } /> + } /> {/* 다크 헤더 페이지 */} diff --git a/frontend/src/pages/curriculum/CurriculumPage.js b/frontend/src/pages/curriculum/CurriculumPage.js index faa0334..c6972a4 100644 --- a/frontend/src/pages/curriculum/CurriculumPage.js +++ b/frontend/src/pages/curriculum/CurriculumPage.js @@ -1,227 +1,369 @@ import { useState, useEffect } from 'react'; import styles from './CurriculumPage.module.css'; -import weekinfo from '../../assets/images/week.png'; -import amimg from '../../assets/images/am.png'; -import pmimg from '../../assets/images/pm.png'; - -function getDayLabel(dateStr) { - const day = new Date(dateStr).getDay(); - const map = { 2: '화요일', 4: '목요일', 6: '토요일' }; - return map[day] || ''; -} +import { authFetch } from '../../utils/Api'; +import LogoImg from '../../assets/images/logo.png'; +import AmImg from '../../assets/images/am.png'; +import PmImg from '../../assets/images/pm.png'; +import Toggle1 from '../../assets/images/icon_togle1.svg'; -function SessionCard({ date, sessions }) { - const [open, setOpen] = useState(false); - const amSession = sessions.find(s => s.dayPart === 'AM'); - const pmSession = sessions.find(s => s.dayPart === 'PM'); - const hasDetail = sessions.some(s => s.title || s.hostName); - const week = sessions[0].week; - - return ( -
-
hasDetail && setOpen(!open)}> -
- - {week}주차 {getDayLabel(date)} 세션 - - {date} -
- {hasDetail && ( - {open ? '▲' : '▼'} - )} -
- -
- - {/* 토글 닫혔을 때 - 제목만 보임 */} - {!open && hasDetail && ( -
- {amSession && amSession.title && ( -
- - 오전 {amSession.title} - +const role = localStorage.getItem('role') || 'MEMBER'; + +const DAY_LABEL = { TUESDAY: '화요일', THURSDAY: '목요일', SATURDAY: '토요일' }; +const STATUS_OPTIONS = ['BEFORE', 'ONGOING', 'AFTER']; +const STATUS_LABEL = { BEFORE: '세션 전', ONGOING: '세션 중', AFTER: '세션 후' }; + +// ── 세션 정보 렌더 (공통) ───────────────────────────── +function SessionInfo({ session, isAdmin }) { + const icon = session.dayPart === 'AM' ? AmImg : PmImg; + const label = session.dayPart === 'AM' ? '오전 세션' : '오후 세션'; + + return ( +
+
+ {label} + {session.title} + {session.hostName}
- )} - {pmSession && pmSession.title && ( -
- - 오후 {pmSession.title} - +
+ 세션 자료 + {session.sessionMaterialUrl + ? {session.sessionMaterialName || '링크'} + : {session.sessionMaterialName || '-'} + }
- )} -
- )} - - {/* 토글 열렸을 때 - 전체 보임 */} - {open && hasDetail && ( -
- {amSession && amSession.title && ( -
-
- - 오전 {amSession.title} - - {amSession.hostName} -
- - +
+ {session.recordingUrl + ? 녹화본 + : - + } + {session.recordingPassword && PW : {session.recordingPassword}}
- )} - {pmSession && pmSession.title && ( - + ); +} + +// ── 부원용 세션 카드 ────────────────────────────────── +function MemberSessionCard({ day }) { + const [isOpen, setIsOpen] = useState(false); + const amSession = day.sessions?.find(s => s.dayPart === 'AM'); + const pmSession = day.sessions?.find(s => s.dayPart === 'PM'); + const weekDay = DAY_LABEL[day.dayOfWeek] || ''; + + return ( +
+
setIsOpen(p => !p)}> +
+ {day.week}주차 {weekDay} 세션 + {day.sessionDate} +
+ toggle
- )} - -
- 과제 -

- {amSession?.description || '아직 공개되지 않았습니다.'} -

-
+
+ + {isOpen && ( +
+ {amSession && } + {pmSession && } + {(day.assignmentName || day.assignmentUrl) && ( +
+ 과제 + {day.assignmentUrl + ? {day.assignmentName || '링크'} + : {day.assignmentName} + } +
+ )} +
+ )}
- )} + ); +} + +// ── 운영진용 세션 카드 ──────────────────────────────── +function AdminSessionCard({ day, onEdit, onDelete }) { + const [isOpen, setIsOpen] = useState(true); + const amSession = day.sessions?.find(s => s.dayPart === 'AM'); + const pmSession = day.sessions?.find(s => s.dayPart === 'PM'); + const weekDay = DAY_LABEL[day.dayOfWeek] || ''; - {!hasDetail && ( -
-

세션 정보가 아직 등록되지 않았습니다.

+ return ( +
+
setIsOpen(p => !p)}> +
+ {day.week}주차 {weekDay} 세션 + {day.sessionDate} +
+ toggle +
+
+ + {isOpen && ( +
+ {amSession && } + {pmSession && } + {(day.assignmentName || day.assignmentUrl) && ( +
+ 과제 + {day.assignmentUrl + ? {day.assignmentName || '링크'} + : {day.assignmentName} + } +
+ )} +
+ + +
+
+ )}
- )} -
- ); + ); } -function WeekSection({ week, dateGroup }) { - return ( -
-
-
- 로고 +// ── 운영진 세션 생성/수정 폼 ────────────────────────── +function SessionForm({ day, week, onClose, onSave }) { + const isEdit = !!day; + const [form, setForm] = useState({ + week: day?.week || week || 1, + sessionDate: day?.sessionDate || '', + generation: day?.generation || 25, + amTitle: day?.sessions?.find(s => s.dayPart === 'AM')?.title || '', + amHost: day?.sessions?.find(s => s.dayPart === 'AM')?.hostName || '', + amMaterialUrl: day?.sessions?.find(s => s.dayPart === 'AM')?.sessionMaterialUrl || '', + amMaterialName: day?.sessions?.find(s => s.dayPart === 'AM')?.sessionMaterialName || '', + amRecordingUrl: day?.sessions?.find(s => s.dayPart === 'AM')?.recordingUrl || '', + amRecordingPw: day?.sessions?.find(s => s.dayPart === 'AM')?.recordingPassword || '', + amStatus: day?.sessions?.find(s => s.dayPart === 'AM')?.status || 'BEFORE', + pmTitle: day?.sessions?.find(s => s.dayPart === 'PM')?.title || '', + pmHost: day?.sessions?.find(s => s.dayPart === 'PM')?.hostName || '', + pmMaterialUrl: day?.sessions?.find(s => s.dayPart === 'PM')?.sessionMaterialUrl || '', + pmMaterialName: day?.sessions?.find(s => s.dayPart === 'PM')?.sessionMaterialName || '', + pmRecordingUrl: day?.sessions?.find(s => s.dayPart === 'PM')?.recordingUrl || '', + pmRecordingPw: day?.sessions?.find(s => s.dayPart === 'PM')?.recordingPassword || '', + pmStatus: day?.sessions?.find(s => s.dayPart === 'PM')?.status || 'BEFORE', + assignmentUrl: day?.assignmentUrl || '', + assignmentName: day?.assignmentName || '', + }); + + // sessionDate 변경 시 요일 자동 계산 + const getWeekDay = (dateStr) => { + if (!dateStr) return ''; + const [year, month, day] = dateStr.split('-').map(Number); + const date = new Date(year, month - 1, day); + const map = { 2: '화요일', 4: '목요일', 6: '토요일' }; + return map[date.getDay()] || ''; + }; + + const handleSave = async () => { + const body = { + generation: Number(form.generation), + week: Number(form.week), + sessionDate: form.sessionDate, + sessions: [ + { + dayPart: 'AM', + title: form.amTitle, + hostName: form.amHost, + sessionMaterialUrl: form.amMaterialUrl, + sessionMaterialName: form.amMaterialName, + recordingUrl: form.amRecordingUrl, + recordingPassword: form.amRecordingPw, + status: form.amStatus, + }, + { + dayPart: 'PM', + title: form.pmTitle, + hostName: form.pmHost, + sessionMaterialUrl: form.pmMaterialUrl, + sessionMaterialName: form.pmMaterialName, + recordingUrl: form.pmRecordingUrl, + recordingPassword: form.pmRecordingPw, + assignmentUrl: form.assignmentUrl, + assignmentName: form.assignmentName, + status: form.pmStatus, + }, + ], + }; + + if (isEdit) { + await authFetch(`/api/curriculums/${day.sessionDate}`, { + method: 'PATCH', + body: JSON.stringify({ + generation: body.generation, + week: body.week, + newSessionDate: form.sessionDate, + sessions: body.sessions, + }), + }); + } else { + await authFetch('/api/curriculums', { + method: 'POST', + body: JSON.stringify(body), + }); + } + onSave(); + onClose(); + }; + + const weeks = [1, 2, 3, 4, 5]; + + return ( +
+
+
+ {form.week}주차 {getWeekDay(form.sessionDate)} 세션 {isEdit ? '수정' : '생성'} +
+ +
+
+ + +
+
+ + setForm({ ...form, sessionDate: e.target.value })} /> +
+
+ + {/* 오전 세션 */} +
+ AM + 오전 세션 +
+ {STATUS_OPTIONS.map(s => ( + + ))} +
+
+
+
setForm({ ...form, amTitle: e.target.value })} />
+
setForm({ ...form, amHost: e.target.value })} />
+
setForm({ ...form, amMaterialName: e.target.value })} />
+
setForm({ ...form, amMaterialUrl: e.target.value })} />
+
setForm({ ...form, amRecordingUrl: e.target.value })} />
+
setForm({ ...form, amRecordingPw: e.target.value })} />
+
+ + {/* 오후 세션 */} +
+ PM + 오후 세션 +
+ {STATUS_OPTIONS.map(s => ( + + ))} +
+
+
+
setForm({ ...form, pmTitle: e.target.value })} />
+
setForm({ ...form, pmHost: e.target.value })} />
+
setForm({ ...form, pmMaterialName: e.target.value })} />
+
setForm({ ...form, pmMaterialUrl: e.target.value })} />
+
setForm({ ...form, pmRecordingUrl: e.target.value })} />
+
setForm({ ...form, pmRecordingPw: e.target.value })} />
+
+ + {/* 과제 */} +
+ 과제 +
setForm({ ...form, assignmentName: e.target.value })} />
+
setForm({ ...form, assignmentUrl: e.target.value })} />
+
+ + + +
-

WEEK {week}

-
-
- {Object.entries(dateGroup).map(([date, sessions]) => ( - - ))} -
-
- ); + ); } +// ── 메인 컴포넌트 ───────────────────────────────────── function CurriculumPage() { - const [sessions, setSessions] = useState([]); - const role = localStorage.getItem('role'); - - useEffect(() => { - setSessions([ - { - id: 1, - week: 1, - sessionDate: '2026-06-23', - dayPart: 'AM', - title: 'HTML/CSS', - hostName: '24기 김서윤', - status: 'AFTER_SESSION', - description: '코딩앵무 클론 코딩, 피로그래밍 페이지 클론 코딩', - sessionMaterialUrl: '#', - sessionMaterialName: 'HTML/CSS', - recordingUrl: '#', - recordingPassword: '%8.D^G&z', - }, - { - id: 2, - week: 1, - sessionDate: '2026-06-23', - dayPart: 'PM', - title: 'Git 기초', - hostName: '24기 한혜담', - status: 'AFTER_SESSION', - description: '코딩앵무 클론 코딩, 피로그래밍 페이지 클론 코딩', - sessionMaterialUrl: '#', - sessionMaterialName: 'Git 기초', - recordingUrl: '#', - recordingPassword: '%8.D^G&z', - }, - { - id: 3, - week: 1, - sessionDate: '2026-06-25', - dayPart: 'AM', - title: '', - hostName: '', - status: 'BEFORE_SESSION', - description: '', - sessionMaterialUrl: '', - sessionMaterialName: '', - recordingUrl: '', - recordingPassword: '', - }, - { - id: 4, - week: 1, - sessionDate: '2026-06-27', - dayPart: 'AM', - title: '', - hostName: '', - status: 'BEFORE_SESSION', - description: '', - sessionMaterialUrl: '', - sessionMaterialName: '', - recordingUrl: '', - recordingPassword: '', - }, - ]); - }, []); - - const grouped = sessions.reduce((acc, session) => { - const week = session.week; - const date = session.sessionDate; - if (!acc[week]) acc[week] = {}; - if (!acc[week][date]) acc[week][date] = []; - acc[week][date].push(session); - return acc; - }, {}); - - return ( -
- {Object.entries(grouped).map(([week, dateGroup]) => ( - - ))} -
- ); + const [days, setDays] = useState([]); + const [showForm, setShowForm] = useState(false); + const [editDay, setEditDay] = useState(null); + const [createWeek, setCreateWeek] = useState(null); + + const fetchDays = async () => { + try { + const res = await authFetch('/api/curriculums'); + const data = await res.json(); + setDays(Array.isArray(data) ? data : []); + } catch (e) {} + }; + + useEffect(() => { fetchDays(); }, []); + + const handleDelete = async (sessionDate) => { + if (!window.confirm('삭제하시겠습니까?')) return; + await authFetch(`/api/curriculums/${sessionDate}`, { method: 'DELETE' }); + fetchDays(); + }; + + // 주차별로 그룹화 + const grouped = days.reduce((acc, day) => { + const week = day.week; + if (!acc[week]) acc[week] = []; + acc[week].push(day); + return acc; + }, {}); + + return ( +
+ {role === 'ADMIN' && ( +
+ +
+ )} + {Object.entries(grouped).map(([week, weekDays]) => ( +
+
+
+ logo + WEEK {week} +
+
+ +
+ {weekDays.map((day, i) => ( + role === 'ADMIN' + ? { setEditDay(d); setCreateWeek(null); setShowForm(true); }} + onDelete={handleDelete} /> + : + ))} +
+
+ ))} + + {showForm && ( + + { setShowForm(false); setEditDay(null); setCreateWeek(null); }} + onSave={fetchDays} + /> + )} +
+ ); } export default CurriculumPage; \ No newline at end of file diff --git a/frontend/src/pages/curriculum/CurriculumPage.module.css b/frontend/src/pages/curriculum/CurriculumPage.module.css index 9eb1097..afd97f9 100644 --- a/frontend/src/pages/curriculum/CurriculumPage.module.css +++ b/frontend/src/pages/curriculum/CurriculumPage.module.css @@ -1,201 +1,447 @@ -.page { - padding: 40px 60px; - background-color: var(--gray50); - min-height: 100vh; +.container { + padding: 40px 60px; + background: var(--gray20); + min-height: calc(100vh - 100px); } +/* 주차 섹션 */ .weekSection { - margin-bottom: 48px; + margin-bottom: 48px; } .weekHeader { - display: flex; - align-items: center; - gap: 12px; - margin-bottom: 24px; + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24px; } -.weekLogo { - width: 36px; - height: 36px; +.weekLeft { + display: flex; + align-items: center; + gap: 12px; } -.weekLogo img { - width: 36px; - height: 36px; +.logoIcon { + width: 40px; + height: 40px; + object-fit: contain; } .weekTitle { - font-family: var(--font-main); - font-size: 30px; - font-weight: 700; - color: var(--black); - margin: 0; + font-family: var(--font-main); + font-size: 1.6rem; + font-weight: 700; + color: var(--black); +} + +.topBar { + display: flex; + justify-content: flex-end; + margin-bottom: 24px; +} + +.createBtn { + padding: 8px 30px; + background: transparent; + border: 1.5px solid var(--dark); + border-radius: 10px; + color: var(--dark); + font-family: var(--font-main); + font-size: 0.95rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; } -.cardGrid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 24px; - align-items: start; +.createBtn:hover { background: var(--dark); color: var(--white); } + +/* 카드 행 */ +.cardsRow { + display: flex; + gap: 20px; + flex-wrap: wrap; + align-items: flex-start; } -.card { - background-color: var(--white); - border-radius: 16px; - padding: 30px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +/* 세션 카드 */ +.sessionCard { + background: var(--white); + border: 1px solid #eee; + border-radius: 20px; + padding: 30px; + max-width: 335px; + flex: 1; + box-shadow: 0 1px 4px rgba(0,0,0,0.06); } .cardHeader { - display: flex; - justify-content: space-between; - align-items: flex-start; - cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + margin-bottom: 0; } -.cardTitleWrap { - display: flex; - align-items: baseline; - gap: 8px; +.cardHeaderLeft { + display: flex; + align-items: center; + gap: 10px; } .cardTitle { - font-family: var(--font-main); - font-size: 20px; - font-weight: 550; - color: var(--black); + font-family: var(--font-main); + font-size: 1.3rem; + font-weight: 650; + color: var(--black); } .cardDate { - font-size: 12px; - color: var(--gray200); + font-family: var(--font-main); + font-size: 0.8rem; + color: #aaa; + margin-left: 10px; } -.toggle { - color: var(--dark); - font-size: 20px; +.cardToggle { + color: var(--dark); + font-size: 0.8rem; } .cardBody { - margin-top: 8px; + display: flex; + flex-direction: column; + gap: 12px; } -.section { - display: flex; - flex-direction: column; - gap: 8px; - margin-bottom: 16px; +.divider { + border: none; + border-top: 1px solid var(--gray200); + margin: 20px 0 20px 0; } -.sectionRow { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; +/* 세션 정보 */ +.sessionInfo { + display: flex; + flex-direction: column; + gap: 4px; } -.sectionTitle { - font-size: 18px; - font-weight: 500; - color: var(--black); +.sessionInfoRow { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.sessionRow { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.sessionTitleRow { margin: 5px 0; - display: flex; - align-items: center; - gap: 6px; } -.host { - font-size: 14px; - color: var(--gray600); - font-weight: 500; +.sessionIcon { + width: 18px; + height: 18px; + object-fit: contain; } -.file_row { - display: flex; - justify-content: space-between; - width: 100%; - text-decoration: none; +.sessionTitle { + font-family: var(--font-main); + font-size: 1.1rem; + font-weight: 550; + color: var(--black); + padding: 5px 0; } -.file_row:hover .file, -.file_row:hover .file_name { - color: var(--main); +.sessionHost { + font-family: var(--font-main); + font-size: 0.9rem; + color: var(--gray600); + margin-left: auto; } -.video_row { +.sessionDetailRow { display: flex; justify-content: space-between; - width: 100%; - text-decoration: none; + padding: 3px 0; } -.video_row:hover .video, -.video_row:hover .video_pw { - color: var(--main); - transition: all 0.1s ease-in-out; +.sessionLink { + font-family: var(--font-main); + font-size: 0.9rem; + color: var(--black); + text-decoration: none; } -.file { - font-size: 16px; - color: var(--black); - font-weight: 500; - margin-left: 27px; +.sessionLink:hover { + color: var(--main); + transition: all ease-in-out 0.2s; + cursor: pointer; } -.file_name { - font-size: 14px; - color: var(--gray600); +.sessionLinkName { + font-family: var(--font-main); + font-size: 0.9rem; + color: var(--black); } -.video { - font-size: 16px; - color: var(--black); - margin-left: 27px; - font-weight: 500; +.sessionRecording { + font-family: var(--font-main); + font-size: 0.9rem; + color: var(--black); } -.video_pw { - font-size: 14px; - color: var(--gray600); +.sessionPw { + font-family: var(--font-main); + font-size: 0.9rem; + color: var(--black); } -.divider { - height: 1.2px; - background-color: var(--gray200); - margin: 20px 0; +/* 과제 */ +.assignmentRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding-top: 4px; + margin-left: 22px; } .assignmentLabel { - font-size: 20px; - font-weight: 700; - color: var(--dark); - margin-left: 25px; + font-family: var(--font-main); + font-size: 1.2rem; + font-weight: 700; + color: var(--dark); +} + +.assignmentSection { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 8px; +} + +/* 운영진 버튼 */ +.adminBtns { + display: flex; + margin: 10px auto; + gap: 8px; +} + +.editBtn { + padding: 6px 20px; + background: transparent; + border: 1.5px solid var(--dark); + border-radius: 10px; + color: var(--dark); + font-family: var(--font-main); + font-size: 0.85rem; + cursor: pointer; +} + +.editBtn:hover { + background: var(--dark); + color: var(--white); + transition: all ease-in-out 0.2s; +} + +.deleteBtn { + padding: 6px 20px; + background: transparent; + border: 1.5px solid var(--dark); + border-radius: 10px; + color: var(--dark); + font-family: var(--font-main); + font-size: 0.85rem; + cursor: pointer; +} + +.deleteBtn:hover { + background: var(--dark); + color: var(--white); + transition: all ease-in-out 0.2s; +} + +/* 세션 생성/수정 폼 */ +.formOverlay { + position: fixed; + inset: 0; + background: rgba(0,0,0,0.3); + display: flex; + align-items: flex-start; + justify-content: center; + overflow-y: auto; + padding: 40px 20px; + z-index: 200; +} + +.formCard { + background: var(--white); + border-radius: 16px; + padding: 40px; + width: 560px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.formTitle { + font-family: var(--font-main); + font-size: 1.3rem; + font-weight: 700; + color: var(--dark); + margin-bottom: 8px; +} + +.formSection { + display: flex; + flex-direction: column; + gap: 4px; +} + +.formSectionTitle { + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; +} + +.amLabel { + font-family: var(--font-main); + font-size: 1rem; + font-weight: 700; + color: var(--main); +} + +.pmLabel { + font-family: var(--font-main); + font-size: 1rem; + font-weight: 700; + color: #666; +} + +.statusBtns { + display: flex; + gap: 6px; + margin-left: auto; +} + +.statusBtn { + padding: 4px 12px; + background: transparent; + border: 1.5px solid var(--main); + border-radius: 50px; + color: var(--main); + font-family: var(--font-main); + font-size: 0.8rem; + cursor: pointer; +} + +.statusActive { + background: var(--main); + color: var(--white); +} + +.formGrid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; +} + +.formLabel { + font-family: var(--font-main); + font-size: 0.85rem; + color: #666; + margin-bottom: 4px; + display: block; +} + +.formInput { + width: 100%; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 8px; + font-family: var(--font-main); + font-size: 0.9rem; + outline: none; + box-sizing: border-box; +} + +.formInput:focus { border-color: var(--main); } + +.saveFormBtn { + padding: 12px 0; + background: transparent; + border: 1.5px solid var(--main); + border-radius: 50px; + color: var(--main); + font-family: var(--font-main); + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + margin-top: 8px; +} + +.saveFormBtn:hover { background: var(--main); color: var(--white); } + +.cancelBtn { + padding: 10px 0; + background: transparent; + border: none; + color: #aaa; + font-family: var(--font-main); + font-size: 0.9rem; + cursor: pointer; + text-align: center; +} + +/* 추가 스타일 */ +.toggleIcon { + width: 14px; + height: 14px; + transition: transform 0.3s ease; + filter: brightness(0) saturate(100%) invert(44%) sepia(60%) saturate(1693%) hue-rotate(89deg) brightness(107%) contrast(95%); +} + +.toggleOpen { + transform: rotate(180deg); +} + +.sessionTitleRow { + display: flex; + align-items: center; + gap: 8px; } -.assignmentText { - font-size: 16px; - color: var(--gray600); - margin: 0; - margin-left: 25px; +.sessionDetailRow { + display: flex; + align-items: center; + gap: 8px; + padding-left: 26px; } -.placeholder { - font-size: 16px; - color: var(--gray200); - text-align: center; - padding: 12px 0; +.sessionDetailLabel { + font-family: var(--font-main); + font-size: 0.9rem; + color: var(--black); + min-width: 50px; } -.amIcon { - width: 20px; - height: 20px; - vertical-align: middle; +.sessionDetailVal { + font-family: var(--font-main); + font-size: 0.9rem; + color: var(--black); } -.pmIcon { - width: 15px; - height: 20px; - vertical-align: middle; - margin-right: 5px; +.formRow2 { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; } \ No newline at end of file From 4d78ec8bf318a84cbba50e30bcdae998df4aaa0e Mon Sep 17 00:00:00 2001 From: plumbestie Date: Tue, 26 May 2026 15:15:06 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[Feat]=20=EC=BB=A4=EB=A6=AC=ED=81=98?= =?UTF-8?q?=EB=9F=BC=20=EC=83=9D=EC=84=B1=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/curriculum/CurriculumCreate.js | 17 --------- .../src/pages/curriculum/CurriculumPage.js | 37 +++++++++--------- .../curriculum/CurriculumPage.module.css | 38 +++++++++++-------- 3 files changed, 43 insertions(+), 49 deletions(-) delete mode 100644 frontend/src/pages/curriculum/CurriculumCreate.js diff --git a/frontend/src/pages/curriculum/CurriculumCreate.js b/frontend/src/pages/curriculum/CurriculumCreate.js deleted file mode 100644 index b832c61..0000000 --- a/frontend/src/pages/curriculum/CurriculumCreate.js +++ /dev/null @@ -1,17 +0,0 @@ -import { useState, useEffect } from 'react'; -import styles from './CurriculumPage.module.css'; - -const CurriculumCreate = () => { - const [title, setTitle] = useState("") - - const onChangeTitle = (e) => { - setTitle(e.target.value); - } - return ( -
- -
- ); -}; - -export default CurriculumCreate; \ No newline at end of file diff --git a/frontend/src/pages/curriculum/CurriculumPage.js b/frontend/src/pages/curriculum/CurriculumPage.js index c6972a4..656f529 100644 --- a/frontend/src/pages/curriculum/CurriculumPage.js +++ b/frontend/src/pages/curriculum/CurriculumPage.js @@ -210,24 +210,27 @@ function SessionForm({ day, week, onClose, onSave }) { return (
-
- {form.week}주차 {getWeekDay(form.sessionDate)} 세션 {isEdit ? '수정' : '생성'} -
+
+ + +
-
-
- - -
-
- - setForm({ ...form, sessionDate: e.target.value })} /> -
-
+
+
+ + +
+
+ + setForm({ ...form, sessionDate: e.target.value })} /> +
+
{/* 오전 세션 */}
diff --git a/frontend/src/pages/curriculum/CurriculumPage.module.css b/frontend/src/pages/curriculum/CurriculumPage.module.css index afd97f9..5adccb5 100644 --- a/frontend/src/pages/curriculum/CurriculumPage.module.css +++ b/frontend/src/pages/curriculum/CurriculumPage.module.css @@ -149,6 +149,7 @@ width: 18px; height: 18px; object-fit: contain; + filter: brightness(0) saturate(100%) invert(44%) sepia(98%) saturate(500%) hue-rotate(90deg) brightness(95%) contrast(110%); } .sessionTitle { @@ -180,7 +181,7 @@ } .sessionLink:hover { - color: var(--main); + color: var(--dark); transition: all ease-in-out 0.2s; cursor: pointer; } @@ -272,7 +273,7 @@ .formOverlay { position: fixed; inset: 0; - background: rgba(0,0,0,0.3); + background: var(--gray20); display: flex; align-items: flex-start; justify-content: center; @@ -316,14 +317,14 @@ font-family: var(--font-main); font-size: 1rem; font-weight: 700; - color: var(--main); + color: var(--dark); } .pmLabel { font-family: var(--font-main); font-size: 1rem; font-weight: 700; - color: #666; + color: var(--dark); } .statusBtns { @@ -335,16 +336,22 @@ .statusBtn { padding: 4px 12px; background: transparent; - border: 1.5px solid var(--main); - border-radius: 50px; - color: var(--main); + border: 1.5px solid var(--dark); + border-radius: 7px; + color: var(--dark); font-family: var(--font-main); font-size: 0.8rem; cursor: pointer; } +.statusBtn:hover { + background: var(--dark); + color: var(--white); + transition: all ease-in-out 0.2s; +} + .statusActive { - background: var(--main); + background: var(--dark); color: var(--white); } @@ -373,23 +380,24 @@ box-sizing: border-box; } -.formInput:focus { border-color: var(--main); } +.formInput:focus { border-color: var(--dark); } .saveFormBtn { - padding: 12px 0; + width: 40%; + margin: 30px auto 0; + padding: 10px 0; background: transparent; - border: 1.5px solid var(--main); - border-radius: 50px; - color: var(--main); + border: 1.5px solid var(--dark); + border-radius: 10px; + color: var(--dark); font-family: var(--font-main); font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.2s; - margin-top: 8px; } -.saveFormBtn:hover { background: var(--main); color: var(--white); } +.saveFormBtn:hover { background: var(--dark); color: var(--white); } .cancelBtn { padding: 10px 0;