WIP working state

This commit is contained in:
Aaron Kimbrell
2026-02-26 09:56:11 -06:00
parent f1847d1f20
commit 8372202d8f
46 changed files with 2622 additions and 434 deletions

View File

@@ -0,0 +1,137 @@
{% extends "base.jinja2" %}
{% block title %}Account - DarkflameServer{% endblock %}
{% block css %}{% endblock %}
{% block content %}
<div class="account-view-container">
<div class="container-fluid">
<a href="/accounts" class="back-link">← Back to Accounts</a>
<div class="card">
<div class="card-header">
<h2>Account #{{ account.id }} - {{ account.name }}</h2>
<p class="text-muted">View account details and manage settings</p>
</div>
<div class="card-body">
<div class="detail-grid">
<div class="detail-item">
<div class="detail-label">Account ID</div>
<div class="detail-value">{{ account.id }}</div>
</div>
<div class="detail-item">
<div class="detail-label">Username</div>
<div class="detail-value">{{ account.name }}</div>
</div>
<div class="detail-item">
<div class="detail-label">Created</div>
<div class="detail-value">{{ account.created_at }}</div>
</div>
<div class="detail-item">
<div class="detail-label">GM Level</div>
<div class="detail-value">
{% if account.gm_level > 0 %}
<span class="badge badge-gm">GM {{ account.gm_level }}</span>
{% else %}
<span class="badge badge-inactive">User</span>
{% endif %}
</div>
</div>
<div class="detail-item">
<div class="detail-label">Ban Status</div>
<div class="detail-value">
{% if account.banned %}
<span class="badge badge-banned">BANNED</span>
{% else %}
<span class="badge badge-active">Active</span>
{% endif %}
</div>
</div>
<div class="detail-item">
<div class="detail-label">Lock Status</div>
<div class="detail-value">
{% if account.locked %}
<span class="badge badge-locked">LOCKED</span>
{% else %}
<span class="badge badge-active">Unlocked</span>
{% endif %}
</div>
</div>
<div class="detail-item">
<div class="detail-label">Mute Expires</div>
<div class="detail-value">
{% if account.mute_expire > 0 %}
<span>{{ account.mute_expire }}</span>
{% else %}
<span class="text-muted">Not muted</span>
{% endif %}
</div>
</div>
</div>
<div class="button-group">
<button class="btn btn-primary" onclick="EditAccount()">Edit Account</button>
{% if not account.banned %}
<button class="btn btn-danger" onclick="BanAccount()">Ban Account</button>
{% else %}
<button class="btn btn-secondary" onclick="UnbanAccount()">Unban Account</button>
{% endif %}
{% if not account.locked %}
<button class="btn btn-danger" onclick="LockAccount()">Lock Account</button>
{% else %}
<button class="btn btn-secondary" onclick="UnlockAccount()">Unlock Account</button>
{% endif %}
</div>
</div>
</div>
<!-- TODO: Add modals for edit, ban, lock operations -->
<!-- TODO: Add character list for this account -->
<!-- TODO: Add login history -->
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function EditAccount() {
alert("Edit functionality coming soon");
// TODO: Open edit modal
}
function BanAccount() {
if (confirm("Are you sure you want to ban this account?")) {
alert("Ban functionality coming soon");
// TODO: Call ban API endpoint
}
}
function UnbanAccount() {
if (confirm("Are you sure you want to unban this account?")) {
alert("Unban functionality coming soon");
// TODO: Call unban API endpoint
}
}
function LockAccount() {
if (confirm("Are you sure you want to lock this account?")) {
alert("Lock functionality coming soon");
// TODO: Call lock API endpoint
}
}
function UnlockAccount() {
if (confirm("Are you sure you want to unlock this account?")) {
alert("Unlock functionality coming soon");
// TODO: Call unlock API endpoint
}
}
</script>
{% endblock %}

View File

@@ -0,0 +1,133 @@
{% extends "base.jinja2" %}
{% block title %}Accounts - DarkflameServer{% endblock %}
{% block css %}{% endblock %}
{% block content %}
<div class="accounts-container">
<div class="container-fluid">
<div class="table-card">
<div class="table-header">
<h2 class="mb-0">Accounts</h2>
<p class="text-muted">View and manage user accounts</p>
</div>
<div class="table-body">
<div class="table-responsive">
<table id="accountsTable" class="table table-dark table-hover mb-0">
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Banned</th>
<th>Locked</th>
<th>GM Level</th>
<th>Mute Expires</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Data populated by DataTables -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
// Initialize DataTable with server-side processing
$('#accountsTable').DataTable({
processing: true,
serverSide: true,
pageLength: 25,
lengthMenu: [10, 25, 50, 100],
ajax: {
url: '/api/tables/accounts',
type: 'POST',
contentType: 'application/json',
data: function(d) {
return JSON.stringify(d);
},
error: function(xhr, error, thrown) {
console.error('Error loading accounts:', error);
if (xhr.status === 401) {
window.location.href = '/login';
}
}
},
columns: [
{ data: 'id' },
{ data: 'name' },
{
data: 'banned',
render: function(data) {
return data ? '<span class="badge badge-banned">Banned</span>' : '<span class="badge bg-success">Active</span>';
}
},
{
data: 'locked',
render: function(data) {
return data ? '<span class="badge badge-locked">Locked</span>' : '<span class="badge bg-secondary">Unlocked</span>';
}
},
{
data: 'gm_level',
render: function(data) {
if (data === 0) return '-';
return '<span class="badge badge-gm">Level ' + data + '</span>';
}
},
{
data: 'mute_expire',
render: function(data) {
if (data === 0) return 'Not Muted';
const now = Math.floor(Date.now() / 1000);
const isMuted = data > now;
const date = new Date(data * 1000);
const dateStr = date.toLocaleString();
if (isMuted) {
return '<span class="badge bg-danger">Muted until ' + dateStr + '</span>';
} else {
return '<span class="badge bg-success">Expired ' + dateStr + '</span>';
}
}
},
{
data: 'created_at',
render: function(data) {
return data ? new Date(data).toLocaleString() : '-';
}
},
{
data: 'id',
render: function(data) {
return '<div class="account-actions">' +
'<button class="btn btn-sm btn-info" onclick="viewAccount(' + data + ')" title="View">👁️</button>' +
'<button class="btn btn-sm btn-warning" onclick="editAccount(' + data + ')" title="Edit">✏️</button>' +
'</div>';
},
orderable: false,
searchable: false
}
],
order: [[0, 'asc']],
stateSave: false
});
});
function viewAccount(id) {
window.location.href = '/accounts/' + id;
}
function editAccount(id) {
alert('Edit account: ' + id);
// TODO: Implement account edit modal
}
</script>
{% endblock %}

View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}DarkflameServer{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.datatables.net/2.3.6/css/dataTables.dataTables.min.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
<link href="https://cdn.datatables.net/v/bs5/jq-3.7.0/dt-2.3.7/b-3.2.6/fh-4.0.6/sc-2.4.3/datatables.min.css" rel="stylesheet" integrity="sha384-XMNDGLb5fN9IqhIrVXOAtGKcz4KCr+JSHXGZ1TDXQPDukbEAfmLPjHdCXhgK93fv" crossorigin="anonymous">
<link rel="stylesheet" href="/css/dashboard.css">
{% block css %}{% endblock %}
</head>
@@ -26,9 +26,8 @@
{% endblock %}
</footer>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.datatables.net/2.3.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
<script src="https://cdn.datatables.net/v/bs5/jq-3.7.0/dt-2.3.7/b-3.2.6/fh-4.0.6/sc-2.4.3/datatables.min.js" integrity="sha384-BPUXtS4tH3onFfu5m+dPbFfpLOXQwSWGwrsNWxOAAwqqJx6tJHhFkGF6uitrmEui" crossorigin="anonymous"></script>
{% block scripts %}{% endblock %}
</body>

View File

@@ -0,0 +1,97 @@
{% extends "base.jinja2" %}
{% block title %}Bug Reports - DarkflameServer{% endblock %}
{% block css %}{% endblock %}
{% block content %}
<div class="bug-reports-container">
<div class="table-card">
<div class="table-header">
<h2 class="mb-0">Bug Reports</h2>
<p class="text-muted">View and manage bug reports from players</p>
</div>
<div class="table-body">
<table id="bugReportsTable" class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Player</th>
<th>Client Version</th>
<th>Submitted</th>
<th>Report Preview</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Data populated by DataTables -->
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
// Initialize DataTable with server-side processing
$('#bugReportsTable').DataTable({
processing: true,
serverSide: true,
pageLength: 25,
lengthMenu: [10, 25, 50, 100],
ajax: {
url: '/api/tables/bug_reports',
type: 'POST',
contentType: 'application/json',
data: function(d) {
return JSON.stringify(d);
}
},
columns: [
{ data: 'id' },
{ data: 'other_player_id' },
{ data: 'client_version' },
{
data: 'submitted',
render: function(data) {
return data ? new Date(data).toLocaleString() : '-';
}
},
{
data: 'body',
render: function(data) {
return '<span class="report-preview" title="' + (data || '') + '">' + (data || '-') + '</span>';
}
},
{
data: 'id',
render: function(data) {
return '<div class="account-actions">' +
'<button class="btn btn-sm btn-info" onclick="viewReport(' + data + ')" title="View">👁️</button>' +
'<button class="btn btn-sm btn-danger" onclick="deleteReport(' + data + ')" title="Delete">🗑️</button>' +
'</div>';
},
orderable: false,
searchable: false
}
],
order: [[0, 'desc']],
stateSave: false
});
});
function viewReport(id) {
alert('View report: ' + id);
// TODO: Implement report view modal
}
function deleteReport(id) {
if (confirm('Are you sure you want to delete this report?')) {
alert('Delete report: ' + id);
// TODO: Implement report deletion
}
}
</script>
{% endblock %}

View File

@@ -0,0 +1,90 @@
{% extends "base.jinja2" %}
{% block title %}Characters - DarkflameServer{% endblock %}
{% block css %}{% endblock %}
{% block content %}
<div class="characters-container">
<div class="table-card">
<div class="table-header">
<h2 class="mb-0">Characters</h2>
<p class="text-muted">View and manage player characters</p>
</div>
<div class="table-body">
<table id="charactersTable" class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Account</th>
<th>Last Login</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Data populated by DataTables -->
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
// Initialize DataTable with server-side processing
$('#charactersTable').DataTable({
processing: true,
serverSide: true,
pageLength: 25,
lengthMenu: [10, 25, 50, 100],
ajax: {
url: '/api/tables/characters',
type: 'POST',
contentType: 'application/json',
data: function(d) {
return JSON.stringify(d);
}
},
columns: [
{ data: 'id' },
{ data: 'name' },
{ data: 'account_name' },
{
data: 'last_login',
render: function(data) {
if (data === 0) return 'Never';
const date = new Date(data * 1000);
return date.toLocaleString();
}
},
{
data: 'id',
render: function(data) {
return '<div class="account-actions">' +
'<button class="btn btn-sm btn-info" onclick="viewCharacter(' + data + ')" title="View">👁️</button>' +
'<button class="btn btn-sm btn-warning" onclick="editCharacter(' + data + ')" title="Edit">✏️</button>' +
'</div>';
},
orderable: false,
searchable: false
}
],
order: [[0, 'asc']],
stateSave: false
});
});
function viewCharacter(id) {
alert('View character: ' + id);
// TODO: Implement character view modal
}
function editCharacter(id) {
alert('Edit character: ' + id);
// TODO: Implement character edit modal
}
</script>
{% endblock %}

View File

@@ -1,27 +1,27 @@
{# Navigation #}
<nav class="navbar navbar-dark bg-dark flex-column" style="width: 280px; height: 100vh; position: fixed; left: 0; top: 0; overflow-y: auto;">
<div class="p-3">
<a class="navbar-brand fw-bold" href="#">🎮 DarkflameServer</a>
<a class="navbar-brand fw-bold" href="/">🎮 DarkflameServer</a>
</div>
<ul class="navbar-nav flex-column w-100 flex-grow-1 p-3">
<li class="nav-item">
<a class="nav-link active" href="#">Home</a>
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#accounts">Accounts</a>
<a class="nav-link" href="/accounts">Accounts</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#characters">Characters</a>
<a class="nav-link" href="/characters">Characters</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#properties">Properties</a>
<a class="nav-link" href="/play_keys">Play Keys</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#players">Players</a>
<a class="nav-link" href="/properties">Properties</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#servers">Servers</a>
<a class="nav-link" href="/bug_reports">Bug Reports</a>
</li>
<li class="nav-item mt-auto">
<a class="nav-link" href="#" id="logoutBtn">Logout</a>

View File

@@ -0,0 +1,95 @@
{% extends "base.jinja2" %}
{% block title %}Play Keys - DarkflameServer{% endblock %}
{% block css %}{% endblock %}
{% block content %}
<div class="play-keys-container">
<div class="table-card">
<div class="table-header">
<h2 class="mb-0">Play Keys</h2>
<p class="text-muted">View and manage play keys</p>
</div>
<div class="table-body">
<table id="playKeysTable" class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Key String</th>
<th>Uses Remaining</th>
<th>Created</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Data populated by DataTables -->
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
// Initialize DataTable with server-side processing
$('#playKeysTable').DataTable({
processing: true,
serverSide: true,
pageLength: 25,
lengthMenu: [10, 25, 50, 100],
ajax: {
url: '/api/tables/play_keys',
type: 'POST',
contentType: 'application/json',
data: function(d) {
return JSON.stringify(d);
}
},
columns: [
{ data: 'id' },
{ data: 'key_string' },
{ data: 'key_uses' },
{
data: 'created_at',
render: function(data) {
return data ? new Date(data).toLocaleString() : '-';
}
},
{
data: 'active',
render: function(data) {
return data ? '<span class="badge badge-active">Active</span>' : '<span class="badge badge-inactive">Inactive</span>';
}
},
{
data: 'id',
render: function(data) {
return '<div class="account-actions">' +
'<button class="btn btn-sm btn-info" onclick="viewKey(' + data + ')" title="View">👁️</button>' +
'<button class="btn btn-sm btn-warning" onclick="editKey(' + data + ')" title="Edit">✏️</button>' +
'</div>';
},
orderable: false,
searchable: false
}
],
order: [[0, 'asc']],
stateSave: false
});
});
function viewKey(id) {
alert('View key: ' + id);
// TODO: Implement key view modal
}
function editKey(id) {
alert('Edit key: ' + id);
// TODO: Implement key edit modal
}
</script>
{% endblock %}

View File

@@ -0,0 +1,92 @@
{% extends "base.jinja2" %}
{% block title %}Properties - DarkflameServer{% endblock %}
{% block css %}{% endblock %}
{% block content %}
<div class="properties-container">
<div class="table-card">
<div class="table-header">
<h2 class="mb-0">Properties</h2>
<p class="text-muted">View and manage player properties</p>
</div>
<div class="table-body">
<table id="propertiesTable" class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Owner ID</th>
<th>Moderation Status</th>
<th>Reputation</th>
<th>Zone</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Data populated by DataTables -->
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
// Initialize DataTable with server-side processing
$('#propertiesTable').DataTable({
processing: true,
serverSide: true,
pageLength: 25,
lengthMenu: [10, 25, 50, 100],
ajax: {
url: '/api/tables/properties',
type: 'POST',
contentType: 'application/json',
data: function(d) {
return JSON.stringify(d);
}
},
columns: [
{ data: 'id' },
{ data: 'name' },
{ data: 'owner_id' },
{
data: 'mod_approved',
render: function(data) {
return data ? '<span class="badge badge-approved">Approved</span>' : '<span class="badge badge-pending">Pending</span>';
}
},
{ data: 'reputation' },
{ data: 'zone_id' },
{
data: 'id',
render: function(data) {
return '<div class="account-actions">' +
'<button class="btn btn-sm btn-info" onclick="viewProperty(' + data + ')" title="View">👁️</button>' +
'<button class="btn btn-sm btn-warning" onclick="editProperty(' + data + ')" title="Edit">✏️</button>' +
'</div>';
},
orderable: false,
searchable: false
}
],
order: [[0, 'asc']],
stateSave: false
});
});
function viewProperty(id) {
alert('View property: ' + id);
// TODO: Implement property view modal
}
function editProperty(id) {
alert('Edit property: ' + id);
// TODO: Implement property edit modal
}
</script>
{% endblock %}