- ASP.NET Core 8 REST API server - 12 API endpoints matching EA Synergy protocol - SQLite database with Entity Framework Core - Web admin panel with Bootstrap 5 - User, Catalog, Session, Purchase management - Comprehensive documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
186 lines
9.6 KiB
Plaintext
186 lines
9.6 KiB
Plaintext
@page
|
|
@model RR3CommunityServer.Pages.PurchasesModel
|
|
@{
|
|
ViewData["Title"] = "Purchase History";
|
|
}
|
|
|
|
<div class="container-fluid mt-4">
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h1>🛒 Purchase History</h1>
|
|
<p class="text-muted">View all in-game purchases</p>
|
|
</div>
|
|
<a href="/admin" class="btn btn-outline-secondary">← Back to Dashboard</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (Model.Purchases.Count == 0)
|
|
{
|
|
<div class="alert alert-info">
|
|
<i class="bi bi-info-circle"></i> No purchases yet. Purchases will appear here when players buy items.
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<!-- Statistics -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4">
|
|
<div class="card border-primary">
|
|
<div class="card-body">
|
|
<h6 class="text-muted">Total Purchases</h6>
|
|
<h2 class="text-primary">@Model.Purchases.Count</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card border-success">
|
|
<div class="card-body">
|
|
<h6 class="text-muted">Approved</h6>
|
|
<h2 class="text-success">@Model.Purchases.Count(p => p.Status == "approved")</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card border-info">
|
|
<div class="card-body">
|
|
<h6 class="text-muted">Total Value</h6>
|
|
<h2 class="text-info">@Model.TotalValue.ToString("C2")</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Purchase List -->
|
|
<div class="card">
|
|
<div class="card-header bg-warning text-dark">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">All Purchases</h5>
|
|
<form method="get" class="d-flex gap-2">
|
|
<input type="text" name="search" value="@Model.SearchQuery" class="form-control form-control-sm" placeholder="Search by SKU or User...">
|
|
<button type="submit" class="btn btn-sm btn-dark">Search</button>
|
|
@if (!string.IsNullOrEmpty(Model.SearchQuery))
|
|
{
|
|
<a href="/admin/purchases" class="btn btn-sm btn-outline-dark">Clear</a>
|
|
}
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>User</th>
|
|
<th>SKU</th>
|
|
<th>Price</th>
|
|
<th>Status</th>
|
|
<th>Purchase Date</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var purchase in Model.Purchases)
|
|
{
|
|
<tr>
|
|
<td><strong>@purchase.Id</strong></td>
|
|
<td>
|
|
@if (purchase.UserId.HasValue)
|
|
{
|
|
<code>User @purchase.UserId</code>
|
|
}
|
|
else
|
|
{
|
|
<span class="text-muted">Unknown</span>
|
|
}
|
|
</td>
|
|
<td><code>@purchase.Sku</code></td>
|
|
<td>
|
|
@if (purchase.Price == 0)
|
|
{
|
|
<span class="text-success"><strong>FREE</strong></span>
|
|
}
|
|
else
|
|
{
|
|
<span>@purchase.Price.ToString("C2")</span>
|
|
}
|
|
</td>
|
|
<td>
|
|
@if (purchase.Status == "approved")
|
|
{
|
|
<span class="badge bg-success">✓ Approved</span>
|
|
}
|
|
else if (purchase.Status == "pending")
|
|
{
|
|
<span class="badge bg-warning">⏳ Pending</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="badge bg-danger">✗ @purchase.Status</span>
|
|
}
|
|
</td>
|
|
<td>@purchase.PurchaseDate.ToString("g")</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-info" data-bs-toggle="modal" data-bs-target="#purchaseModal@(purchase.Id)">
|
|
<i class="bi bi-eye"></i> Details
|
|
</button>
|
|
<form method="post" asp-page-handler="Delete" class="d-inline">
|
|
<input type="hidden" name="purchaseId" value="@purchase.Id" />
|
|
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Delete this purchase record?')">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Purchase Details Modal -->
|
|
<div class="modal fade" id="purchaseModal@(purchase.Id)" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-warning">
|
|
<h5 class="modal-title">Purchase Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<dl class="row">
|
|
<dt class="col-sm-3">Purchase ID:</dt>
|
|
<dd class="col-sm-9">@purchase.Id</dd>
|
|
|
|
<dt class="col-sm-3">User ID:</dt>
|
|
<dd class="col-sm-9">@(purchase.UserId?.ToString() ?? "N/A")</dd>
|
|
|
|
<dt class="col-sm-3">SKU:</dt>
|
|
<dd class="col-sm-9"><code>@purchase.Sku</code></dd>
|
|
|
|
<dt class="col-sm-3">Price:</dt>
|
|
<dd class="col-sm-9">@purchase.Price.ToString("C2")</dd>
|
|
|
|
<dt class="col-sm-3">Status:</dt>
|
|
<dd class="col-sm-9">
|
|
<span class="badge bg-@(purchase.Status == "approved" ? "success" : "warning")">
|
|
@purchase.Status
|
|
</span>
|
|
</dd>
|
|
|
|
<dt class="col-sm-3">Purchase Date:</dt>
|
|
<dd class="col-sm-9">@purchase.PurchaseDate.ToString("F")</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|