feat(ui): discover table fits viewport (merged 영상 column)
Merge thumbnail+title+channel into one "영상" column like 수집함 so the table fits without horizontal scroll. Promote .coll-table/.row-sel to global style.css for reuse across list pages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f3be6bd803
commit
652d25a483
@ -402,6 +402,13 @@ tbody td { padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--border); }
|
|||||||
tbody tr { transition: background 0.15s ease; }
|
tbody tr { transition: background 0.15s ease; }
|
||||||
tbody tr:hover { background: var(--hover); }
|
tbody tr:hover { background: var(--hover); }
|
||||||
|
|
||||||
|
/* Compact list table: number/status cells stay one line, "영상" cell stays wide */
|
||||||
|
.coll-table th, .coll-table td { white-space: nowrap; vertical-align: middle; }
|
||||||
|
.coll-table td:first-child, .coll-table th:first-child { white-space: normal; width: 42%; min-width: 280px; }
|
||||||
|
.coll-table thead th { background: var(--surface-2); }
|
||||||
|
.row-sel { padding: 4px 6px; background: var(--surface-2); border: 1px solid var(--border-strong); border-radius: 6px; color: var(--text); font-size: 0.78rem; outline: none; max-width: 110px; }
|
||||||
|
.row-sel option { background: var(--surface); color: var(--text); }
|
||||||
|
|
||||||
/* ===== Modal (reusable) ===== */
|
/* ===== Modal (reusable) ===== */
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
display: none; position: fixed; inset: 0; z-index: 1000;
|
display: none; position: fixed; inset: 0; z-index: 1000;
|
||||||
|
|||||||
@ -84,23 +84,21 @@
|
|||||||
|
|
||||||
<!-- 결과 테이블 -->
|
<!-- 결과 테이블 -->
|
||||||
<div class="card p-0" style="overflow-x:auto;">
|
<div class="card p-0" style="overflow-x:auto;">
|
||||||
<table class="w-full" style="border-collapse:collapse; text-align:left;">
|
<table class="w-full coll-table" style="border-collapse:collapse; text-align:left;">
|
||||||
<thead style="background:var(--surface-2); border-bottom:1px solid var(--glass-border);">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="p-3 text-sm font-bold text-muted">썸네일</th>
|
<th>영상</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">제목</th>
|
<th>구독자</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">채널</th>
|
<th>조회수</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">구독자</th>
|
<th>시간당</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">조회수</th>
|
<th>배율</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">시간당</th>
|
<th>업로드</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">배율</th>
|
<th>상태</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">업로드</th>
|
<th style="text-align:right;">관리</th>
|
||||||
<th class="p-3 text-sm font-bold text-muted">상태</th>
|
|
||||||
<th class="p-3 text-sm font-bold text-muted">관리</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="resultBody">
|
<tbody id="resultBody">
|
||||||
<tr><td colspan="10" class="p-8 text-center text-muted">로딩 중...</td></tr>
|
<tr><td colspan="8" class="p-8 text-center text-muted">로딩 중...</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -156,7 +154,7 @@
|
|||||||
let data;
|
let data;
|
||||||
try { data = await api(API + '/discover?' + p.toString()); }
|
try { data = await api(API + '/discover?' + p.toString()); }
|
||||||
catch(e){
|
catch(e){
|
||||||
body.innerHTML = '<tr><td colspan="10" class="p-8 text-center text-danger">불러오기 실패: '+esc(e.message)+'</td></tr>';
|
body.innerHTML = '<tr><td colspan="8" class="p-8 text-center text-danger">불러오기 실패: '+esc(e.message)+'</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderRows(data || []);
|
renderRows(data || []);
|
||||||
@ -174,38 +172,37 @@
|
|||||||
const body = document.getElementById('resultBody');
|
const body = document.getElementById('resultBody');
|
||||||
document.getElementById('resultCount').textContent = list.length + '건';
|
document.getElementById('resultCount').textContent = list.length + '건';
|
||||||
if(list.length===0){
|
if(list.length===0){
|
||||||
body.innerHTML = '<tr><td colspan="10" class="p-8 text-center text-muted">조건에 맞는 발굴 후보가 없습니다.</td></tr>';
|
body.innerHTML = '<tr><td colspan="8" class="p-8 text-center text-muted">조건에 맞는 발굴 후보가 없습니다.</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
body.innerHTML = list.map(v=>{
|
body.innerHTML = list.map(v=>{
|
||||||
const statusOpts = STATUS_KEYS.map(k=>
|
const statusOpts = STATUS_KEYS.map(k=>
|
||||||
`<option value="${k}" ${v.interestStatus===k?'selected':''}>${STATUS_LABEL[k]}</option>`).join('');
|
`<option value="${k}" ${v.interestStatus===k?'selected':''}>${STATUS_LABEL[k]}</option>`).join('');
|
||||||
const star = v.bookmarked ? '#f59e0b' : 'var(--text-muted)';
|
const star = v.bookmarked ? '#f59e0b' : 'var(--text-muted)';
|
||||||
const shortsBadge = v.isShorts ? '<span style="font-size:0.65rem; background:#7C3AED33; color:#a78bfa; padding:1px 5px; border-radius:4px; margin-left:4px;">Shorts</span>' : '';
|
const shortsBadge = v.isShorts ? '<span class="badge badge-primary" style="margin-left:5px;">Shorts</span>' : '';
|
||||||
return `<tr style="border-bottom:1px solid var(--glass-border);">
|
return `<tr>
|
||||||
<td class="p-3">
|
<td>
|
||||||
<div style="position:relative; cursor:pointer; width:96px;" data-vid="${v.videoId}" data-title="${esc(v.title)}" onclick="openVideoModal(this.dataset.vid, this.dataset.title)">
|
<div class="flex items-center gap-3" style="min-width:0;">
|
||||||
<img src="${esc(v.thumbnailUrl)}" style="width:96px; height:54px; object-fit:cover; border-radius:6px;">
|
<img src="${esc(v.thumbnailUrl)}" data-vid="${v.videoId}" data-title="${esc(v.title)}" onclick="openVideoModal(this.dataset.vid, this.dataset.title)"
|
||||||
|
style="width:84px; height:47px; object-fit:cover; border-radius:6px; cursor:pointer; flex-shrink:0;">
|
||||||
|
<div style="min-width:0;">
|
||||||
|
<div class="font-semibold text-sm" style="display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; line-height:1.35;">
|
||||||
|
<a href="https://www.youtube.com/watch?v=${v.videoId}" target="_blank" class="hover:underline">${esc(v.title)}</a>${shortsBadge}
|
||||||
|
</div>
|
||||||
|
<a href="${v.ytChannelId?('https://www.youtube.com/channel/'+v.ytChannelId):'#'}" target="_blank" class="hover:underline truncate" style="color:var(--text-3); font-size:0.72rem; display:block; margin-top:2px;">${esc(v.channelTitle||'-')}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="p-3" style="max-width:280px;">
|
<td class="text-sm">${fmt(v.subscriberCount)}</td>
|
||||||
<div class="font-bold text-sm" style="display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;">
|
<td class="text-sm">${fmt(v.viewCount)}</td>
|
||||||
<a href="https://www.youtube.com/watch?v=${v.videoId}" target="_blank" class="hover:underline">${esc(v.title)}</a>${shortsBadge}
|
<td class="text-sm">${fmt(v.viewsPerHour)}</td>
|
||||||
</div>
|
<td class="text-sm">${ratioBadge(v.viewsPerSubRatio)}</td>
|
||||||
</td>
|
<td class="text-sm text-muted">${fmtDate(v.publishedAt)}</td>
|
||||||
<td class="p-3 text-sm text-muted" style="max-width:130px;">
|
<td><select class="row-sel" onchange="setStatus(${v.id}, this.value)">${statusOpts}</select></td>
|
||||||
<a href="${v.ytChannelId?('https://www.youtube.com/channel/'+v.ytChannelId):'#'}" target="_blank" class="hover:underline truncate" style="color:var(--text-2);">${esc(v.channelTitle||'-')}</a>
|
<td>
|
||||||
</td>
|
<div class="flex items-center gap-1 justify-end">
|
||||||
<td class="p-3 text-sm">${fmt(v.subscriberCount)}</td>
|
<a class="btn btn-primary" style="padding:0.4rem;" title="재가공" href="/rework/${v.id}"><i data-lucide="wand-2" style="width:15px;"></i></a>
|
||||||
<td class="p-3 text-sm">${fmt(v.viewCount)}</td>
|
<button class="btn btn-secondary" style="padding:0.4rem;" title="북마크" onclick="toggleBookmark(${v.id}, ${!!v.bookmarked})"><i data-lucide="star" style="width:15px; color:${star};"></i></button>
|
||||||
<td class="p-3 text-sm">${fmt(v.viewsPerHour)}</td>
|
|
||||||
<td class="p-3 text-sm">${ratioBadge(v.viewsPerSubRatio)}</td>
|
|
||||||
<td class="p-3 text-sm text-muted">${fmtDate(v.publishedAt)}</td>
|
|
||||||
<td class="p-3"><select class="row-sel" onchange="setStatus(${v.id}, this.value)">${statusOpts}</select></td>
|
|
||||||
<td class="p-3">
|
|
||||||
<div class="flex items-center gap-1">
|
|
||||||
<a class="btn btn-primary p-2" title="재가공" href="/rework/${v.id}"><i data-lucide="wand-2" style="width:15px;"></i></a>
|
|
||||||
<button class="btn btn-secondary p-2" title="북마크" onclick="toggleBookmark(${v.id}, ${!!v.bookmarked})"><i data-lucide="star" style="width:15px; color:${star};"></i></button>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user