Files
tinker_tickets/views/admin/UserActivityView.php
T
jared 2e450dc01d Apply web_template gap analysis improvements (P1-P3)
P1-A: Fix CSP - add fonts.googleapis.com to style-src, fonts.gstatic.com to font-src
P1-B: CSRF token rotation - add rotateToken() to CsrfMiddleware; bootstrap.php rotates
      after successful validation and stores in $GLOBALS['_new_csrf_token']; add
      apiRespond() helper to append token to responses; lt.api interceptor in
      layout_footer.php auto-updates window.CSRF_TOKEN from responses
P1-C: Styled 403/404 error views with TDS layout instead of raw text; index.php now
      uses requireAdmin() helper eliminating 7 duplicated guard blocks (P3-D)
P2-A: Remove duplicate JS-generated keyboard help modal from keyboard-shortcuts.js;
      '?' key now routes to static #lt-keys-help modal in footer
P2-B: Asset versioning driven by config ASSET_VERSION key; base.css and base.js get
      ?v= cache-busting in layout_header.php
P2-C: Add data-theme="dark" to <html> tag to prevent FOUC on light-mode users
P2-E: Escape status value in dashboard.js hover preview class attribute via lt.escHtml()
P2-F: Replace bespoke showLoadingOverlay() with lt-spinner / lt-loading-text from
      base.css; add .lt-loading-overlay wrapper CSS to dashboard.css
P2-G: Add keyboard-shortcuts.js to all 7 admin views so J/K nav and ? help work
P3-A: APP_NAME, APP_SUBTITLE, APP_VERSION driven from config.php; layout header/footer
      use config values instead of hardcoded strings
P3-G: Replace custom initTableSorting() with lt.sortTable.init() which manages aria-sort

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:02:40 -04:00

117 lines
5.0 KiB
PHP

<?php
require_once __DIR__ . '/../../middleware/SecurityHeadersMiddleware.php';
require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
$nonce = SecurityHeadersMiddleware::getNonce();
$pageTitle = 'User Activity';
$activeNav = 'admin-user-activity';
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
include __DIR__ . '/../../views/layout_header.php';
?>
<div class="lt-page-header">
<div class="lt-flex lt-flex-gap-sm lt-flex-align-center">
<a href="/" class="lt-btn lt-btn-ghost lt-btn-sm">&larr; Dashboard</a>
<span class="lt-text-muted lt-text-xs">/</span>
<span class="lt-text-muted lt-text-xs">Admin: User Activity</span>
</div>
</div>
<div class="lt-frame">
<span class="lt-frame-bl">╚</span><span class="lt-frame-br">╝</span>
<div class="lt-section-header">User Activity Report</div>
<div class="lt-section-body">
<!-- Date filter -->
<form method="GET" class="lt-flex lt-flex-wrap lt-flex-gap-sm lt-mb-md" role="search">
<div class="lt-form-group" style="margin:0">
<label class="lt-label" for="date_from">Date From</label>
<input type="date" name="date_from" id="date_from" class="lt-input lt-input-sm"
value="<?= htmlspecialchars($dateRange['from'] ?? '') ?>">
</div>
<div class="lt-form-group" style="margin:0">
<label class="lt-label" for="date_to">Date To</label>
<input type="date" name="date_to" id="date_to" class="lt-input lt-input-sm"
value="<?= htmlspecialchars($dateRange['to'] ?? '') ?>">
</div>
<div class="lt-flex lt-flex-gap-sm lt-flex-align-center" style="align-self:flex-end">
<button type="submit" class="lt-btn lt-btn-primary lt-btn-sm">APPLY</button>
<a href="?" class="lt-btn lt-btn-ghost lt-btn-sm">RESET</a>
</div>
</form>
<!-- Summary stats -->
<?php if (!empty($userStats)): ?>
<div class="lt-stats-grid lt-mb-md">
<div class="lt-stat-card">
<div class="lt-stat-icon lt-text-cyan">[ # ]</div>
<div class="lt-stat-info">
<div class="lt-stat-value"><?= count($userStats) ?></div>
<div class="lt-stat-label">Active Users</div>
</div>
</div>
<div class="lt-stat-card">
<div class="lt-stat-icon">[ + ]</div>
<div class="lt-stat-info">
<div class="lt-stat-value"><?= array_sum(array_column($userStats, 'tickets_created')) ?></div>
<div class="lt-stat-label">Total Created</div>
</div>
</div>
<div class="lt-stat-card">
<div class="lt-stat-icon lt-text-muted">[ OK ]</div>
<div class="lt-stat-info">
<div class="lt-stat-value"><?= array_sum(array_column($userStats, 'tickets_resolved')) ?></div>
<div class="lt-stat-label">Total Resolved</div>
</div>
</div>
<div class="lt-stat-card">
<div class="lt-stat-icon lt-text-amber">[ > ]</div>
<div class="lt-stat-info">
<div class="lt-stat-value"><?= array_sum(array_column($userStats, 'comments_added')) ?></div>
<div class="lt-stat-label">Total Comments</div>
</div>
</div>
</div>
<?php endif ?>
<!-- User activity table -->
<div class="lt-table-wrap">
<table class="lt-table lt-table-responsive" aria-label="User activity">
<thead>
<tr>
<th scope="col">User</th>
<th scope="col">Tickets Created</th>
<th scope="col">Tickets Resolved</th>
<th scope="col">Comments</th>
<th scope="col">Assigned</th>
<th scope="col">Last Activity</th>
</tr>
</thead>
<tbody>
<?php if (empty($userStats)): ?>
<tr><td colspan="6" class="lt-empty">No user activity data available.</td></tr>
<?php else: foreach ($userStats as $u): ?>
<tr>
<td data-label="User">
<strong><?= htmlspecialchars($u['display_name'] ?? $u['username']) ?></strong>
<?php if ($u['is_admin']): ?>
<span class="lt-badge lt-badge-admin lt-text-xs">ADMIN</span>
<?php endif ?>
</td>
<td data-label="Created"><span class="lt-text-cyan"><?= (int)($u['tickets_created'] ?? 0) ?></span></td>
<td data-label="Resolved"><span class="lt-text-muted"><?= (int)($u['tickets_resolved'] ?? 0) ?></span></td>
<td data-label="Comments"><span class="lt-text-amber"><?= (int)($u['comments_added'] ?? 0) ?></span></td>
<td data-label="Assigned"><?= (int)($u['tickets_assigned'] ?? 0) ?></td>
<td data-label="Last Activity" class="lt-text-xs lt-text-muted">
<?= $u['last_activity'] ? date('M d, Y H:i', strtotime($u['last_activity'])) : 'Never' ?>
</td>
</tr>
<?php endforeach; endif ?>
</tbody>
</table>
</div>
</div>
</div>
<?php include __DIR__ . '/../../views/layout_footer.php'; ?>