File size: 5,452 Bytes
7f22d3c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<!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>