GitHub Action
Sync from GitHub Actions (Clean Commit)
7f22d3c
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>TUM Search - Admin Console</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<style> .scroll-box { height: 70vh; overflow-y: auto; border: 1px solid #ddd; padding: 10px; } </style>
</head>
<body class="bg-dark text-light">
<div id="app" class="container-fluid py-4">
<h2 class="mb-4">⚡️ Admin Control Center</h2>
<div class="row">
<div class="col-md-6">
<div class="card bg-secondary text-white">
<div class="card-header d-flex justify-content-between">
<h5 class="mb-0">Space R (Reference / Anchors)</h5>
<button class="btn btn-sm btn-light" @click="loadData('R', true)">Refresh</button>
</div>
<div class="card-body scroll-box">
<div v-for="item in rItems" :key="item.id" class="card bg-dark mb-2 border-secondary">
<div class="card-body py-2">
<div class="small text-muted mb-1">ID: {{item.id}}</div>
<div>{{ (item.payload.content || item.payload.content_preview || 'No Content').substring(0, 80) }}...</div>
<div class="mt-2 text-end">
<button class="btn btn-sm btn-danger py-0" @click="deleteItem('R', item.id)">Delete</button>
</div>
</div>
</div>
<button v-if="rNext" class="btn btn-outline-light w-100 mt-2" @click="loadData('R')">Load More</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card bg-light text-dark">
<div class="card-header d-flex justify-content-between">
<h5 class="mb-0">Space X (Search Pool)</h5>
<button class="btn btn-sm btn-dark" @click="loadData('X', true)">Refresh</button>
</div>
<div class="card-body scroll-box">
<div v-for="item in xItems" :key="item.id" class="card mb-2 border-0 shadow-sm">
<div class="card-body py-2">
<div class="small text-muted mb-1">ID: {{item.id}} | Score: {{item.score.toFixed(4)}}</div>
<div class="fw-bold">{{ item.payload.url }}</div>
<div>{{ (item.payload.content_preview || '').substring(0, 80) }}...</div>
<div class="mt-2 d-flex justify-content-end gap-2">
<button class="btn btn-sm btn-success py-0" @click="promote(item.id)">Promote to R</button>
<button class="btn btn-sm btn-danger py-0" @click="deleteItem('X', item.id)">Delete</button>
</div>
</div>
</div>
<button v-if="xNext" class="btn btn-outline-dark w-100 mt-2" @click="loadData('X')">Load More</button>
</div>
</div>
</div>
</div>
</div>
<script src="config.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;
createApp({
data() { return { rItems: [], xItems: [], rNext: null, xNext: null } },
mounted() { this.loadData('R'); this.loadData('X'); },
methods: {
async loadData(space, refresh=false) {
if(refresh) {
if(space==='R') { this.rItems=[]; this.rNext=null; }
else { this.xItems=[]; this.xNext=null; }
}
const offset = space==='R' ? this.rNext : this.xNext;
const res = await fetch(API_CONFIG.getURL(`${API_CONFIG.endpoints.admin.browse}?space=${space}&limit=50&offset=${offset||''}`));
const data = await res.json();
if(space === 'R') {
this.rItems.push(...data.items);
this.rNext = data.next_offset;
} else {
this.xItems.push(...data.items);
this.xNext = data.next_offset;
}
},
async deleteItem(space, id) {
if(!confirm('Are you sure?')) return;
await fetch(API_CONFIG.getURL(`${API_CONFIG.endpoints.admin.delete}?space=${space}&id=${id}`), { method: 'DELETE' });
alert('Deleted.');
this.loadData(space, true); // Refresh
},
async promote(id) {
const formData = new FormData(); formData.append('id', id);
await fetch(API_CONFIG.getURL(API_CONFIG.endpoints.admin.promote), { method: 'POST', body: formData });
alert('Promoted to Space R. Global recalculation triggered.');
this.loadData('R', true); // Refresh R
}
}
}).mount('#app');
</script>
</body>
</html>