WHAT WAS DONE: - Added Admin tier (1000) back to tier dropdown - Added is_staff toggle checkbox in Actions column - Created POST route /admin/players/:discord_id/staff - Updated query to include is_staff from users table - Both tier and staff status tracked separately WHY: - Trinity needs ability to assign Admin tier to team members - Staff can also be subscribers - need to track both - Example: Moderator who also pays for Elemental tier - Separate tracking prevents conflating employment and subscription HOW IT WORKS: - Tier dropdown shows ALL tiers including Admin - Staff checkbox toggles is_staff on users table - Both changes create separate audit log entries - Staff flag independent of subscription tier DATABASE REQUIREMENT: - Requires migration: ALTER TABLE users ADD COLUMN is_staff BOOLEAN DEFAULT FALSE; - Must be run before deploying this code FEATURES: - Admin tier assignable via dropdown - Staff toggle with visual checkbox - Both tracked in audit log separately - Tier + Staff shown side-by-side in Actions column IMPACT: - Can now hire staff and track their employment - Staff can also be subscribers without conflict - Clear separation of concerns - Ready for team expansion FILES MODIFIED: - services/arbiter-3.0/src/views/admin/players/_table_body.ejs - services/arbiter-3.0/src/routes/admin/players.js DEPLOYMENT STEPS: 1. Run database migration (ADD is_staff column) 2. Deploy code files 3. Restart arbiter-3 service Signed-off-by: Claude (Chronicler #52) <claude@firefrostgaming.com>
70 lines
4.1 KiB
Plaintext
70 lines
4.1 KiB
Plaintext
<% if (players.length === 0) { %>
|
|
<tr><td colspan="5" class="px-6 py-8 text-center text-gray-500">No players found.</td></tr>
|
|
<% } %>
|
|
<% players.forEach(player => { %>
|
|
<tr class="border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800/50">
|
|
<td class="px-6 py-4 font-mono text-xs"><%= player.discord_id %></td>
|
|
<td class="px-6 py-4">
|
|
<div class="flex items-center gap-3">
|
|
<img src="https://mc-heads.net/avatar/<%= player.minecraft_uuid %>/32" class="w-8 h-8 rounded" alt="<%= player.minecraft_username %>" onerror="this.src='https://mc-heads.net/avatar/steve/32'">
|
|
<div>
|
|
<div class="font-medium"><%= player.minecraft_username || 'Unlinked' %></div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<% const tier = TIER_INFO[player.tier_level] || { name: 'None', path: 'universal' }; %>
|
|
<span class="px-2.5 py-1 text-xs rounded-full font-medium border
|
|
<%= tier.path === 'fire' ? 'bg-orange-100 text-orange-700 border-orange-200 dark:bg-orange-900/30 dark:text-orange-400 dark:border-orange-800/50' :
|
|
tier.path === 'frost' ? 'bg-cyan-100 text-cyan-700 border-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400 dark:border-cyan-800/50' :
|
|
'bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-900/30 dark:text-purple-400 dark:border-purple-800/50' %>">
|
|
<%= tier.name %>
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<span class="inline-flex items-center gap-1.5">
|
|
<span class="w-2 h-2 rounded-full <%= player.status === 'active' || player.status === 'lifetime' ? 'bg-green-500' : player.status === 'grace_period' ? 'bg-yellow-500' : 'bg-red-500' %>"></span>
|
|
<%= player.status || 'Unknown' %>
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 text-right">
|
|
<div class="flex items-center justify-end gap-2">
|
|
<!-- Tier Change Dropdown -->
|
|
<form hx-post="/admin/players/<%= player.discord_id %>/tier"
|
|
hx-trigger="change from:select"
|
|
hx-on::after-request="htmx.ajax('GET', '/admin/players/table', {target:'#player-table-body', swap:'innerHTML'})">
|
|
<select class="bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded px-2 py-1 text-sm"
|
|
name="tier_level">
|
|
<option value="">Tier...</option>
|
|
<% Object.keys(TIER_INFO).sort((a, b) => parseInt(b) - parseInt(a)).forEach(tierLevel => {
|
|
const tierData = TIER_INFO[tierLevel];
|
|
%>
|
|
<option value="<%= tierLevel %>" <%= player.tier_level == tierLevel ? 'selected' : '' %>><%= tierData.name %></option>
|
|
<% }); %>
|
|
</select>
|
|
</form>
|
|
|
|
<!-- Staff Toggle -->
|
|
<label class="flex items-center gap-1 cursor-pointer" title="Staff Member">
|
|
<input type="checkbox"
|
|
class="rounded border-gray-300 dark:border-gray-600"
|
|
<%= player.is_staff ? 'checked' : '' %>
|
|
hx-post="/admin/players/<%= player.discord_id %>/staff"
|
|
hx-trigger="change"
|
|
hx-on::after-request="htmx.ajax('GET', '/admin/players/table', {target:'#player-table-body', swap:'innerHTML'})">
|
|
<span class="text-xs text-gray-600 dark:text-gray-400">Staff</span>
|
|
</label>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<% }) %>
|
|
<tr class="bg-gray-50 dark:bg-gray-800/50">
|
|
<td colspan="5" class="px-6 py-3 text-center">
|
|
<button hx-get="/admin/players/table?page=<%= page + 1 %>&search=<%= search %>"
|
|
hx-target="#player-table-body"
|
|
class="text-sm font-medium text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
|
|
Load More Players ↓
|
|
</button>
|
|
</td>
|
|
</tr>
|