feat(assignments): 주차별 과제 → 학습 문서(과제집) 연결 고리
All checks were successful
CI / backend-test (push) Successful in 1m21s
CI / frontend-build (push) Successful in 47s
CI / backend-dep-scan (push) Successful in 5s

과제 페이지에 '이 주차 학습 문서' 링크를 붙였다. 학생이 과제만 보고
'뭘 보고 하나' 헷갈리던 문제 해결. 주차별 과제집(week1~4-assignments)이
있는 주차에만 뜨고, 없는 주차(5~8)엔 안 뜬다.
This commit is contained in:
AWESOMEDEV 2026-07-23 13:23:13 +09:00
parent d931281946
commit 4a7e75f92d
2 changed files with 58 additions and 0 deletions

View File

@ -1,6 +1,7 @@
// : () , // : () ,
// ( + ) · . // ( + ) · .
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import client from '../api/client'; import client from '../api/client';
import Badge from '../components/Badge'; import Badge from '../components/Badge';
@ -119,6 +120,7 @@ export default function AssignmentsPage() {
const [week, setWeek] = useState(1); const [week, setWeek] = useState(1);
const [assignments, setAssignments] = useState([]); const [assignments, setAssignments] = useState([]);
const [mySubmissions, setMySubmissions] = useState([]); const [mySubmissions, setMySubmissions] = useState([]);
const [docs, setDocs] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
// . // .
@ -146,6 +148,12 @@ export default function AssignmentsPage() {
}; };
}, [week]); }, [week]);
// ( ) .
// API ( ).
useEffect(() => {
client.get('/documents').then((res) => setDocs(res.data)).catch(() => {});
}, []);
// : API. // : API.
// assignmentId () . // assignmentId () .
function findSubmission(assignmentId) { function findSubmission(assignmentId) {
@ -157,6 +165,9 @@ export default function AssignmentsPage() {
const percent = const percent =
assignments.length === 0 ? 0 : Math.round((submittedCount / assignments.length) * 100); assignments.length === 0 ? 0 : Math.round((submittedCount / assignments.length) * 100);
// ( ). .
const weekDocs = docs.filter((d) => d.week === week);
// () . // () .
const groups = new Map(); const groups = new Map();
for (const assignment of assignments) { for (const assignment of assignments) {
@ -199,6 +210,20 @@ export default function AssignmentsPage() {
))} ))}
</div> </div>
{/* " " .
주차별 과제집이 있는 1~4주차엔 링크가, 없는 주차(5~8) 뜬다. */}
{weekDocs.length > 0 && (
<div className="assignment-doc-links">
{weekDocs.map((d) => (
<Link key={d.slug} to={`/docs/${d.slug}`} className="card assignment-doc-link">
<span className="assignment-doc-badge">📚 학습 문서</span>
<span className="assignment-doc-title">{d.title}</span>
<span className="assignment-doc-cta">과제 전에 먼저 읽어 보세요 </span>
</Link>
))}
</div>
)}
{loading ? ( {loading ? (
<p className="empty">불러오는 ...</p> <p className="empty">불러오는 ...</p>
) : assignments.length === 0 ? ( ) : assignments.length === 0 ? (

View File

@ -1588,3 +1588,36 @@ inline-code, .icode {
align-items: center; align-items: center;
} }
.helper-send:disabled { opacity: 0.5; cursor: default; } .helper-send:disabled { opacity: 0.5; cursor: default; }
/* ── 과제 → 학습 문서 연결 고리 ──────────────────────────────────── */
.assignment-doc-links { margin: 14px 0; }
.assignment-doc-link {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
text-decoration: none;
color: var(--ink);
word-break: keep-all; /* 한글 단어 중간 줄바꿈 금지 */
transition: border-color 0.15s, box-shadow 0.15s;
}
.assignment-doc-link + .assignment-doc-link { margin-top: 10px; }
.assignment-doc-link:hover {
border-color: var(--primary);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.assignment-doc-badge {
flex: none;
font-weight: 700;
color: var(--primary);
}
.assignment-doc-title { font-weight: 600; }
.assignment-doc-cta {
margin-left: auto;
color: var(--muted);
font-size: 13px;
white-space: nowrap;
}
@media (max-width: 520px) {
.assignment-doc-cta { margin-left: 0; } /* 좁은 화면에선 CTA를 다음 줄로 */
}