Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ public ResponseEntity<List<PendingListResponse>> findAll(@AuthAdmin TuripMember
return ResponseEntity.ok(adminContentPendingService.findAll());
}

@GetMapping("/my-list")
public ResponseEntity<List<PendingListResponse>> findMyPending(@AuthAdmin TuripMember admin) {
return ResponseEntity.ok(adminContentPendingService.findByValidatorAccount(admin.getMember().getAccount()));
}

@GetMapping("/my-count")
public ResponseEntity<Long> countMyPending(@AuthAdmin TuripMember admin) {
return ResponseEntity.ok(adminContentPendingService.countMyPending(admin.getMember().getAccount()));
}

@GetMapping("/{id}")
public ResponseEntity<ContentPendingResponse> findById(
@AuthAdmin TuripMember admin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public record PendingListResponse(
String videoTitle,
String channelName,
String collectorNickname,
String validatorNickname,
LocalDateTime createdAt
) {

Expand All @@ -17,6 +18,7 @@ public static PendingListResponse from(ContentPending p) {
p.getContentData().video().title(),
p.getContentData().video().channelName(),
p.getCollectorAccount().getNickname(),
p.getValidatorAccount() != null ? p.getValidatorAccount().getNickname() : null,
p.getCreatedAt()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ public List<PendingListResponse> findAll() {
.toList();
}

public List<PendingListResponse> findByValidatorAccount(Account validatorAccount) {
return contentPendingRepository.findAllByStatusAndValidatorAccountOrderByIdDesc(
ContentPendingStatus.PENDING,
validatorAccount
)
.stream()
.map(PendingListResponse::from)
.toList();
}

public long countMyPending(Account validatorAccount) {
return contentPendingRepository.countByStatusAndValidatorAccount(
ContentPendingStatus.PENDING,
validatorAccount
);
}

public List<MyCollectContentResponse> getMyHistory(Account account) {
return contentPendingRepository.findAllByCollectorAccountOrderByIdDesc(account)
.stream()
Expand Down
14 changes: 7 additions & 7 deletions backend/turip-admin/src/main/resources/templates/admin/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ <h1>Turip Admin</h1>
<span class="badge" id="pendingBadge">0</span>
</div>
<div class="notification-dropdown" id="notifDropdown">
<div class="dropdown-header">최신 검수 요청</div>
<div class="dropdown-header"> 검수 요청</div>
<div class="dropdown-body" id="notifList">
<div class="empty-state">내역이 없습니다.</div>
</div>
Expand Down Expand Up @@ -268,7 +268,7 @@ <h2>반갑습니다, <span style="color:#667eea;" th:text="${admin.member.accoun
async function fetchRecentPending() {
const container = document.getElementById('notifList');
try {
const res = await fetch('/api/v1/admin/content-pendings/list');
const res = await fetch('/api/v1/admin/content-pendings/my-list');

if (!res.ok) {
const errorData = await res.json().catch(() => null);
Expand All @@ -279,7 +279,7 @@ <h2>반갑습니다, <span style="color:#667eea;" th:text="${admin.member.accoun
const list = await res.json();

if (!list || list.length === 0) {
container.innerHTML = '<div class="empty-state">새로운 요청이 없습니다.</div>';
container.innerHTML = '<div class="empty-state">나에게 배정된 요청이 없습니다.</div>';
return;
}

Expand Down Expand Up @@ -309,16 +309,16 @@ <h2>반갑습니다, <span style="color:#667eea;" th:text="${admin.member.accoun

async function updatePendingCount() {
try {
const res = await fetch('/api/v1/admin/content-pendings/list');
const res = await fetch('/api/v1/admin/content-pendings/my-count');

if (!res.ok) {
throw new Error(`서버 오류 (${res.status})`);
}

const list = await res.json();
const count = await res.json();
const badge = document.getElementById('pendingBadge');
if (list.length > 0) {
badge.innerText = list.length > 99 ? '99+' : list.length;
if (count > 0) {
badge.innerText = count > 99 ? '99+' : count;
badge.style.display = 'block';
} else {
badge.style.display = 'none';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,46 @@
font-weight: 600;
color: #444;
}

.validator-name {
font-weight: 600;
color: #6e8efb;
}

.not-assigned {
color: #999;
font-style: italic;
}

/* 탭 스타일 */
.tab-container {
display: flex;
gap: 10px;
margin-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}

.tab-button {
padding: 12px 24px;
background: transparent;
border: none;
color: #888;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: 0.2s;
border-bottom: 3px solid transparent;
margin-bottom: -2px;
}

.tab-button:hover {
color: #6e8efb;
}

.tab-button.active {
color: #6e8efb;
border-bottom-color: #6e8efb;
}
</style>
</head>
<body>
Expand All @@ -131,13 +171,19 @@ <h1 style="color: #667eea; font-size: 22px; font-weight: 800; margin:0;">Turip A
<div class="admin-card">
<h3 class="section-title">검수 대기 목록</h3>

<div class="tab-container">
<button class="tab-button active" data-tab="my" onclick="switchTab('my')">내 검수 요청</button>
<button class="tab-button" data-tab="all" onclick="switchTab('all')">전체 검수 요청</button>
</div>

<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th style="width: 50px;">ID</th>
<th>영상 정보</th>
<th style="width: 200px;">수집자</th>
<th style="width: 150px;">수집자</th>
<th style="width: 150px;">담당자</th>
<th style="width: 100px; text-align: center;">상태</th>
<th style="width: 120px; text-align: right;">관리</th>
</tr>
Expand All @@ -150,10 +196,29 @@ <h3 class="section-title">검수 대기 목록</h3>
</div>

<script>
async function loadPendingList() {
let currentTab = 'my'; // 기본 탭은 '내 검수 요청'

function switchTab(tab) {
currentTab = tab;

// 탭 버튼 active 클래스 변경
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
document.querySelector(`[data-tab="${tab}"]`).classList.add('active');

// 데이터 로드
loadPendingList(tab);
}

async function loadPendingList(tab = 'my') {
const tbody = document.getElementById('pendingTableBody');
const endpoint = tab === 'my'
? '/api/v1/admin/content-pendings/my-list'
: '/api/v1/admin/content-pendings/list';

try {
const res = await fetch('/api/v1/admin/content-pendings/list');
const res = await fetch(endpoint);

if (!res.ok) {
const errorData = await res.json().catch(() => null);
Expand All @@ -164,7 +229,10 @@ <h3 class="section-title">검수 대기 목록</h3>
const list = await res.json();

if (list.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" class="text-center py-5 text-muted">검수 대기 중인 콘텐츠가 없습니다.</td></tr>';
const emptyMessage = tab === 'my'
? '나에게 배정된 검수 요청이 없습니다.'
: '검수 대기 중인 콘텐츠가 없습니다.';
tbody.innerHTML = `<tr><td colspan="6" class="text-center py-5 text-muted">${emptyMessage}</td></tr>`;
return;
}

Expand Down Expand Up @@ -192,6 +260,16 @@ <h3 class="section-title">검수 대기 목록</h3>
tdCollector.textContent = item.collectorNickname;
tr.appendChild(tdCollector);

const tdValidator = document.createElement('td');
if (item.validatorNickname) {
tdValidator.className = 'validator-name';
tdValidator.textContent = item.validatorNickname;
} else {
tdValidator.className = 'not-assigned';
tdValidator.textContent = '미배정';
}
tr.appendChild(tdValidator);

const tdStatus = document.createElement('td');
tdStatus.className = 'text-center';
const statusBadge = document.createElement('span');
Expand All @@ -213,11 +291,11 @@ <h3 class="section-title">검수 대기 목록</h3>
});
} catch (e) {
console.error("데이터 로드 실패:", e);
tbody.innerHTML = '<tr><td colspan="5" class="text-center py-5 text-danger">⚠️ 데이터를 불러오는데 실패했습니다: ' + e.message + '</td></tr>';
tbody.innerHTML = '<tr><td colspan="6" class="text-center py-5 text-danger">⚠️ 데이터를 불러오는데 실패했습니다: ' + e.message + '</td></tr>';
}
}

document.addEventListener('DOMContentLoaded', loadPendingList);
document.addEventListener('DOMContentLoaded', () => loadPendingList('my'));
</script>
</body>
</html>
Loading
Loading