Skip to content

Commit e9ce4cc

Browse files
committed
feat : 프론트 토론글 생성, 수정 시 언어 선택 기능 구현
1 parent 3d4c38a commit e9ce4cc

File tree

1 file changed

+92
-14
lines changed

1 file changed

+92
-14
lines changed

src/main/resources/templates/test-submit.html

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -372,16 +372,18 @@
372372
}
373373

374374
.discussion-item {
375-
background: #222;
376-
border-radius: 10px;
375+
background: #23272f;
376+
border-radius: 12px;
377377
margin-bottom: 18px;
378-
padding: 18px 16px;
379-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.10);
380-
transition: box-shadow 0.2s;
378+
padding: 22px 18px 18px 18px;
379+
box-shadow: 0 4px 18px rgba(0,0,0,0.18);
380+
border: 1.5px solid #2d323c;
381+
transition: box-shadow 0.2s, border 0.2s;
381382
}
382383

383384
.discussion-item:hover {
384-
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
385+
box-shadow: 0 8px 28px rgba(0,0,0,0.28);
386+
border-color: #00d084;
385387
}
386388

387389
/* Header (profile/nickname) */
@@ -390,6 +392,7 @@
390392
align-items: center;
391393
gap: 8px;
392394
margin-bottom: 2px;
395+
margin-top: 8px;
393396
}
394397

395398
.discussion-header img {
@@ -779,6 +782,19 @@
779782
border-color: #00d084;
780783
font-weight: 700;
781784
}
785+
.lang-tag {
786+
display: inline-block;
787+
background: #2d323c;
788+
color: #00e09e;
789+
font-size: 13px;
790+
font-weight: 600;
791+
border-radius: 12px;
792+
padding: 4px 16px;
793+
margin-bottom: 14px;
794+
margin-top: 2px;
795+
margin-right: 0;
796+
letter-spacing: 0.5px;
797+
}
782798
</style>
783799
</head>
784800

@@ -800,6 +816,10 @@
800816
<li>메모리 제한: <span id="prob-mem">--</span> KB</li>
801817
<li>카테고리: <span id="prob-cats">--</span></li>
802818
</ul>
819+
820+
<div id="prob-image-wrapper">
821+
<img id="prob-image" src="" alt="문제 이미지" style="max-width: 100%; height: auto;" />
822+
</div>
803823
</div>
804824

805825
<div id="discussion-content" class="left-content">
@@ -816,10 +836,6 @@
816836
<div id="discussion-pagination"></div>
817837
</div>
818838

819-
<div id="prob-image-wrapper">
820-
<img id="prob-image" src="" alt="문제 이미지" style="max-width: 100%; height: auto;" />
821-
</div>
822-
823839
</div>
824840
<div class="right">
825841
<div class="section">
@@ -873,12 +889,48 @@ <h3>채점 결과</h3>
873889

874890
const DEFAULT_PROFILE_IMG = "/static/images/user-icon.png"; // 임의 프사 경로
875891

892+
// ====== 언어 목록 불러오기 및 전역 저장 ======
893+
async function loadLanguages() {
894+
try {
895+
const res = await fetch('/api/languages');
896+
const data = await res.json();
897+
if (data.success) {
898+
window.languageList = data.result;
899+
} else {
900+
window.languageList = [];
901+
}
902+
} catch {
903+
window.languageList = [];
904+
}
905+
}
906+
// 언어 드롭다운 HTML 생성
907+
function getLanguageSelectHtml(selectedId) {
908+
if (!window.languageList) return '';
909+
return `
910+
<select class="discussion-language-select">
911+
${window.languageList.map(lang =>
912+
`<option value="${lang.id}" ${lang.id === selectedId ? 'selected' : ''}>
913+
${lang.name} ${lang.version}
914+
</option>`
915+
).join('')}
916+
</select>
917+
`;
918+
}
919+
// 언어 id로 name+version 반환
920+
function getLanguageTagById(id) {
921+
if (!window.languageList) return '';
922+
const lang = window.languageList.find(l => l.id === id);
923+
return lang ? `${lang.name} ${lang.version}` : '';
924+
}
925+
876926
function tokenHeader() {
877927
const token = sessionStorage.getItem('accessToken');
878928
return token ? { 'Authorization': token.startsWith('Bearer ') ? token : 'Bearer ' + token } : {};
879929
}
880930

881-
window.addEventListener('DOMContentLoaded', () => {
931+
window.addEventListener('DOMContentLoaded', async () => {
932+
await loadLanguages();
933+
renderDiscussionCreateBox(); // 언어 목록 로드 후 호출
882934
const pid = new URLSearchParams(location.search).get('problemId');
883935
if (pid) {
884936
window.problemId = pid;
@@ -1180,6 +1232,7 @@ <h3>채점 결과</h3>
11801232
listEl.innerHTML = discussions.map(d => {
11811233
return `
11821234
<div class="discussion-item" data-discussion-id="${d.discussionId}">
1235+
<span class="lang-tag">${getLanguageTagById(d.languageId)}</span>
11831236
<div class="discussion-header">
11841237
<img src="${d.userInfo.profileImageUrl || DEFAULT_PROFILE_IMG}" alt="profile">
11851238
<span class="nickname">${d.userInfo.nickname}</span>
@@ -1409,10 +1462,22 @@ <h3>채점 결과</h3>
14091462
const oldContent = contentEl.textContent;
14101463
// 이미 수정창이 열려있으면 return
14111464
if (item.querySelector('.edit-input-box')) return;
1412-
// textarea + 취소/저장 버튼
1465+
// 기존 언어 id (discussion-item만)
1466+
let oldLangId = 1;
1467+
if (item.classList.contains('discussion-item')) {
1468+
// lang-tag에서 id 추출
1469+
const langTag = item.querySelector('.lang-tag');
1470+
if (langTag) {
1471+
const text = langTag.textContent.trim();
1472+
const found = window.languageList?.find(l => text.startsWith(l.name));
1473+
if (found) oldLangId = found.id;
1474+
}
1475+
}
1476+
// textarea + 언어 드롭다운 + 취소/저장 버튼
14131477
const box = document.createElement('div');
14141478
box.className = 'edit-input-box';
14151479
box.innerHTML = `
1480+
<div style="margin-bottom:8px;">${item.classList.contains('discussion-item') ? getLanguageSelectHtml(oldLangId) : ''}</div>
14161481
<textarea class="edit-input-text" style="width:100%;min-height:60px;">${oldContent}</textarea>
14171482
<div style="display:flex;gap:8px;justify-content:flex-end;">
14181483
<button class="edit-cancel-btn">취소</button>
@@ -1431,13 +1496,19 @@ <h3>채점 결과</h3>
14311496
// 저장
14321497
box.querySelector('.edit-save-btn').onclick = async () => {
14331498
const newContent = box.querySelector('.edit-input-text').value.trim();
1499+
let newLangId = oldLangId;
1500+
if (item.classList.contains('discussion-item')) {
1501+
newLangId = +box.querySelector('.discussion-language-select').value;
1502+
}
14341503
if (!newContent) return alert('내용을 입력하세요.');
1435-
let url, method = 'PUT', payload = { languageId: 1, content: newContent };
1504+
let url, method = 'PUT', payload = { languageId: newLangId, content: newContent };
14361505
if (item.classList.contains('discussion-item')) {
14371506
url = `/api/problems/${window.problemId}/discussions/${e.target.dataset.id}`;
14381507
} else {
14391508
const discussionId = item.closest('.discussion-item').dataset.discussionId;
14401509
url = `/api/problems/${window.problemId}/discussions/${discussionId}/replies/${e.target.dataset.id}`;
1510+
// 대댓글/댓글은 언어 변경 불가 (기존대로 1)
1511+
payload.languageId = 1;
14411512
}
14421513
const res = await fetch(url, {
14431514
method,
@@ -1659,7 +1730,13 @@ <h3>채점 결과</h3>
16591730

16601731
function renderDiscussionCreateBox() {
16611732
const box = document.getElementById('discussion-create-box');
1733+
if (!window.languageList || window.languageList.length === 0) {
1734+
box.innerHTML = '<div style="color:#aaa;">언어 목록을 불러오는 중...</div>';
1735+
return;
1736+
}
1737+
// 언어 드롭다운 + textarea
16621738
box.innerHTML = `
1739+
<div style="margin-bottom:8px;">${getLanguageSelectHtml(window.languageList?.[0]?.id || 1)}</div>
16631740
<textarea id="discussion-create-text" class="reply-input-text" rows="4" placeholder="토론글을 작성해보세요"></textarea>
16641741
<div style="display:flex;gap:10px;justify-content:flex-end;">
16651742
<button id="discussion-create-cancel" class="reply-cancel-btn">취소</button>
@@ -1673,6 +1750,7 @@ <h3>채점 결과</h3>
16731750
// 작성
16741751
box.querySelector('#discussion-create-submit').onclick = async () => {
16751752
const content = box.querySelector('#discussion-create-text').value.trim();
1753+
const langId = +box.querySelector('.discussion-language-select').value;
16761754
if (!content) return alert('내용을 입력하세요.');
16771755
const token = sessionStorage.getItem('accessToken');
16781756
const res = await fetch(`/api/problems/${window.problemId}/discussions`, {
@@ -1681,7 +1759,7 @@ <h3>채점 결과</h3>
16811759
'Content-Type': 'application/json',
16821760
...tokenHeader()
16831761
},
1684-
body: JSON.stringify({ languageId: 1, content })
1762+
body: JSON.stringify({ languageId: langId, content })
16851763
});
16861764
if (res.ok) {
16871765
// 성공: 입력창 비우고 목록 새로고침

0 commit comments

Comments
 (0)