feat: Add Admin tier to dropdown and staff tracking

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>
This commit is contained in:
Claude (Chronicler #52)
2026-04-01 15:33:06 +00:00
parent 085e60e748
commit aeeaa14865
2 changed files with 58 additions and 15 deletions

View File

@@ -28,20 +28,33 @@
</span>
</td>
<td class="px-6 py-4 text-right">
<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="">Change Tier...</option>
<% Object.keys(TIER_INFO).sort((a, b) => parseInt(b) - parseInt(a)).forEach(tierLevel => {
const tierData = TIER_INFO[tierLevel];
if (tierLevel == 1000) return; // Skip Admin tier
%>
<option value="<%= tierLevel %>" <%= player.tier_level == tierLevel ? 'selected' : '' %>><%= tierData.name %> ($<%= tierData.price %>)</option>
<% }); %>
</select>
</form>
<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>
<% }) %>