smodusermc commited on
Commit
f3f3424
·
verified ·
1 Parent(s): bc8ca28

Create players.html

Browse files
Files changed (1) hide show
  1. templates/players.html +283 -0
templates/players.html ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block page_title %}Players{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="players-container">
7
+ <!-- Online Players -->
8
+ <div class="card">
9
+ <div class="card-header">
10
+ <h3>Online Players ({{ online|length }}/2)</h3>
11
+ <button class="btn btn-sm" onclick="refreshPlayers()">Refresh</button>
12
+ </div>
13
+ <div class="card-body">
14
+ {% if online %}
15
+ <div class="players-grid" id="online-players">
16
+ {% for player in online %}
17
+ <div class="player-card online">
18
+ <img src="https://mc-heads.net/avatar/{{ player.name }}/64" alt="{{ player.name }}" class="player-avatar-lg">
19
+ <div class="player-info">
20
+ <h4>{{ player.name }}</h4>
21
+ <span class="player-status online">Online</span>
22
+ <div class="player-meta">
23
+ <span>Joins: {{ player.join_count }}</span>
24
+ <span>Playtime: {{ player.play_time }}</span>
25
+ </div>
26
+ </div>
27
+ <div class="player-actions">
28
+ <button class="btn btn-sm btn-warning" onclick="kickPlayer('{{ player.name }}')">Kick</button>
29
+ <button class="btn btn-sm btn-danger" onclick="banPlayer('{{ player.name }}')">Ban</button>
30
+ {% if player.is_op %}
31
+ <button class="btn btn-sm" onclick="deopPlayer('{{ player.name }}')">Remove OP</button>
32
+ {% else %}
33
+ <button class="btn btn-sm" onclick="opPlayer('{{ player.name }}')">Make OP</button>
34
+ {% endif %}
35
+ </div>
36
+ </div>
37
+ {% endfor %}
38
+ </div>
39
+ {% else %}
40
+ <div class="empty-state">
41
+ <span class="empty-icon">👥</span>
42
+ <p>No players online</p>
43
+ </div>
44
+ {% endif %}
45
+ </div>
46
+ </div>
47
+
48
+ <!-- All Known Players -->
49
+ <div class="card">
50
+ <div class="card-header">
51
+ <h3>All Players ({{ all_players|length }})</h3>
52
+ <input type="text" id="player-search" placeholder="Search players..." onkeyup="searchPlayers()">
53
+ </div>
54
+ <div class="card-body">
55
+ <div class="table-container">
56
+ <table class="data-table" id="players-table">
57
+ <thead>
58
+ <tr>
59
+ <th>Player</th>
60
+ <th>Status</th>
61
+ <th>First Seen</th>
62
+ <th>Last Seen</th>
63
+ <th>Playtime</th>
64
+ <th>Joins</th>
65
+ <th>Actions</th>
66
+ </tr>
67
+ </thead>
68
+ <tbody>
69
+ {% for player in all_players %}
70
+ <tr class="{{ 'banned' if player.is_banned else '' }}">
71
+ <td>
72
+ <div class="player-cell">
73
+ <img src="https://mc-heads.net/avatar/{{ player.name }}/32" alt="{{ player.name }}">
74
+ <span>{{ player.name }}</span>
75
+ {% if player.is_op %}<span class="badge op">OP</span>{% endif %}
76
+ {% if player.is_banned %}<span class="badge banned">Banned</span>{% endif %}
77
+ </div>
78
+ </td>
79
+ <td>
80
+ <span class="status-badge {{ 'online' if player.online else 'offline' }}">
81
+ {{ 'Online' if player.online else 'Offline' }}
82
+ </span>
83
+ </td>
84
+ <td>{{ player.first_seen[:10] if player.first_seen != 'Unknown' else '-' }}</td>
85
+ <td>{{ player.last_seen[:10] if player.last_seen != 'Unknown' else '-' }}</td>
86
+ <td>{{ player.play_time }}</td>
87
+ <td>{{ player.join_count }}</td>
88
+ <td>
89
+ <div class="action-buttons">
90
+ {% if player.is_banned %}
91
+ <button class="btn btn-xs btn-success" onclick="pardonPlayer('{{ player.name }}')">Unban</button>
92
+ {% else %}
93
+ <button class="btn btn-xs btn-danger" onclick="banPlayer('{{ player.name }}')">Ban</button>
94
+ {% endif %}
95
+ <button class="btn btn-xs" onclick="showPlayerDetails('{{ player.name }}')">Details</button>
96
+ </div>
97
+ </td>
98
+ </tr>
99
+ {% endfor %}
100
+ </tbody>
101
+ </table>
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ <!-- Whitelist & Ops -->
107
+ <div class="two-column">
108
+ <div class="card">
109
+ <div class="card-header">
110
+ <h3>Operators</h3>
111
+ </div>
112
+ <div class="card-body">
113
+ <ul class="simple-list">
114
+ {% for op in ops %}
115
+ <li>
116
+ <img src="https://mc-heads.net/avatar/{{ op }}/24" alt="{{ op }}">
117
+ <span>{{ op }}</span>
118
+ <button class="btn btn-xs" onclick="deopPlayer('{{ op }}')">Remove</button>
119
+ </li>
120
+ {% else %}
121
+ <li class="empty">No operators</li>
122
+ {% endfor %}
123
+ </ul>
124
+ <div class="add-form">
125
+ <input type="text" id="new-op" placeholder="Player name">
126
+ <button class="btn btn-sm btn-primary" onclick="addOp()">Add OP</button>
127
+ </div>
128
+ </div>
129
+ </div>
130
+
131
+ <div class="card">
132
+ <div class="card-header">
133
+ <h3>Banned Players</h3>
134
+ </div>
135
+ <div class="card-body">
136
+ <ul class="simple-list">
137
+ {% for ban in banned %}
138
+ <li>
139
+ <img src="https://mc-heads.net/avatar/{{ ban.name }}/24" alt="{{ ban.name }}">
140
+ <span>{{ ban.name }}</span>
141
+ <button class="btn btn-xs btn-success" onclick="pardonPlayer('{{ ban.name }}')">Unban</button>
142
+ </li>
143
+ {% else %}
144
+ <li class="empty">No banned players</li>
145
+ {% endfor %}
146
+ </ul>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <!-- Player Details Modal -->
153
+ <div class="modal" id="player-modal">
154
+ <div class="modal-content">
155
+ <div class="modal-header">
156
+ <h3 id="modal-player-name">Player Details</h3>
157
+ <button class="modal-close" onclick="closeModal()">&times;</button>
158
+ </div>
159
+ <div class="modal-body" id="modal-player-content">
160
+ Loading...
161
+ </div>
162
+ </div>
163
+ </div>
164
+ {% endblock %}
165
+
166
+ {% block scripts %}
167
+ <script>
168
+ const socket = io();
169
+
170
+ socket.on('players_update', (data) => {
171
+ refreshPlayers();
172
+ });
173
+
174
+ async function refreshPlayers() {
175
+ location.reload();
176
+ }
177
+
178
+ async function kickPlayer(name) {
179
+ const reason = prompt('Kick reason (optional):') || 'Kicked by admin';
180
+ const response = await fetch('/api/players/kick', {
181
+ method: 'POST',
182
+ headers: {'Content-Type': 'application/json'},
183
+ body: JSON.stringify({player: name, reason: reason})
184
+ });
185
+ const data = await response.json();
186
+ showToast(data.message, data.success ? 'success' : 'error');
187
+ }
188
+
189
+ async function banPlayer(name) {
190
+ const reason = prompt('Ban reason (optional):') || 'Banned by admin';
191
+ if (confirm(`Are you sure you want to ban ${name}?`)) {
192
+ const response = await fetch('/api/players/ban', {
193
+ method: 'POST',
194
+ headers: {'Content-Type': 'application/json'},
195
+ body: JSON.stringify({player: name, reason: reason})
196
+ });
197
+ const data = await response.json();
198
+ showToast(data.message, data.success ? 'success' : 'error');
199
+ if (data.success) refreshPlayers();
200
+ }
201
+ }
202
+
203
+ async function pardonPlayer(name) {
204
+ const response = await fetch('/api/players/pardon', {
205
+ method: 'POST',
206
+ headers: {'Content-Type': 'application/json'},
207
+ body: JSON.stringify({player: name})
208
+ });
209
+ const data = await response.json();
210
+ showToast(data.message, data.success ? 'success' : 'error');
211
+ if (data.success) refreshPlayers();
212
+ }
213
+
214
+ async function opPlayer(name) {
215
+ const response = await fetch('/api/players/op', {
216
+ method: 'POST',
217
+ headers: {'Content-Type': 'application/json'},
218
+ body: JSON.stringify({player: name})
219
+ });
220
+ const data = await response.json();
221
+ showToast(data.message, data.success ? 'success' : 'error');
222
+ if (data.success) refreshPlayers();
223
+ }
224
+
225
+ async function deopPlayer(name) {
226
+ const response = await fetch('/api/players/deop', {
227
+ method: 'POST',
228
+ headers: {'Content-Type': 'application/json'},
229
+ body: JSON.stringify({player: name})
230
+ });
231
+ const data = await response.json();
232
+ showToast(data.message, data.success ? 'success' : 'error');
233
+ if (data.success) refreshPlayers();
234
+ }
235
+
236
+ async function addOp() {
237
+ const name = document.getElementById('new-op').value.trim();
238
+ if (name) {
239
+ await opPlayer(name);
240
+ document.getElementById('new-op').value = '';
241
+ }
242
+ }
243
+
244
+ function searchPlayers() {
245
+ const search = document.getElementById('player-search').value.toLowerCase();
246
+ const rows = document.querySelectorAll('#players-table tbody tr');
247
+
248
+ rows.forEach(row => {
249
+ const name = row.querySelector('.player-cell span').textContent.toLowerCase();
250
+ row.style.display = name.includes(search) ? '' : 'none';
251
+ });
252
+ }
253
+
254
+ function showPlayerDetails(name) {
255
+ document.getElementById('modal-player-name').textContent = name;
256
+ document.getElementById('modal-player-content').innerHTML = `
257
+ <div class="player-detail-view">
258
+ <img src="https://mc-heads.net/body/${name}/100" alt="${name}">
259
+ <div class="detail-actions">
260
+ <button class="btn" onclick="sendCommand('tp @s ${name}')">Teleport To</button>
261
+ <button class="btn" onclick="sendCommand('effect give ${name} minecraft:regeneration 60 2')">Heal</button>
262
+ <button class="btn" onclick="sendCommand('clear ${name}')">Clear Inventory</button>
263
+ </div>
264
+ </div>
265
+ `;
266
+ document.getElementById('player-modal').classList.add('active');
267
+ }
268
+
269
+ function closeModal() {
270
+ document.getElementById('player-modal').classList.remove('active');
271
+ }
272
+
273
+ async function sendCommand(cmd) {
274
+ const response = await fetch('/api/console/send', {
275
+ method: 'POST',
276
+ headers: {'Content-Type': 'application/json'},
277
+ body: JSON.stringify({command: cmd})
278
+ });
279
+ const data = await response.json();
280
+ showToast(data.message, data.success ? 'success' : 'error');
281
+ }
282
+ </script>
283
+ {% endblock %}