feat(dashboard): show frontend/backend app badges on recent logins, full-width ext bar
Universal: Auto Version Bump / Version Bump (push) Successful in 8s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 52s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled

- Recent logins table now parses action log message JSON to display
  Admin/Site badge indicating where each login occurred
- Login query uses exact match on LOGGED_IN key (excludes logout events)
- Login limit increased from 5 to 10
- Info bar reverted from stacked column to horizontal row layout
- Extension version bar uses flex: 1 1 0 for full-width auto-sized cells
- Updated CHANGELOG
This commit is contained in:
Jonathan Miller
2026-06-23 07:44:50 -05:00
parent 95e9e618aa
commit 0aeaea208f
5 changed files with 51 additions and 9 deletions
+4
View File
@@ -30,6 +30,7 @@
- **Backup bridge plugin** — discovers MokoSuiteBackup's BackupStatusHelper and sends status in heartbeat payloads
- **Activity log** — blockchain-style hash chain for tamper detection in MokoSuiteHQ
- **Dev domain in heartbeat** — client sends dev alias to HQ for display on dashboard
- **Login app badges** — recent logins table shows Admin/Site badge parsed from Joomla action log message JSON
### Changed
- **Plugin install** — self-healing: extracts plugin zips from package on every update, creates missing extension records with namespace
@@ -41,6 +42,9 @@
- **Heartbeat** — correct URL (suite.dev), correct API route (mokosuitehq), correct headers (X-MokoSuite-*), fresh RSA key pair
- **Date formats** — all templates use Joomla locale-aware DATE_FORMAT_LC2/LC4
- **Domains** — updated from waas.dev to suite.dev.mokoconsulting.tech throughout
- **Dashboard info bar** — reverted stacked layout; info items back to horizontal row
- **Extension version bar** — full-width auto-sized strip with equal-width cells and border separators
- **Recent logins** — exact match on LOGGED_IN key (excludes logout noise), limit increased to 10
### Removed
- **Helpdesk/tickets** — migrated to MokoSuiteCRM (issue #67)
@@ -454,7 +454,7 @@ class DashboardModel extends BaseDatabaseModel
])
->from($db->quoteName('#__action_logs', 'a'))
->leftJoin($db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('u.id') . ' = ' . $db->quoteName('a.user_id'))
->where($db->quoteName('a.message_language_key') . ' LIKE ' . $db->quote('%LOGIN%'))
->where($db->quoteName('a.message_language_key') . ' = ' . $db->quote('PLG_ACTIONLOG_JOOMLA_USER_LOGGED_IN'))
->order($db->quoteName('a.log_date') . ' DESC')
->setLimit($limit);
$db->setQuery($query);
@@ -56,7 +56,7 @@ class HtmlView extends BaseHtmlView
}
}
catch (\Throwable $e) {}
$this->recentLogins = $model->getRecentLogins(5);
$this->recentLogins = $model->getRecentLogins(10);
$this->pendingUpdates = $model->getPendingUpdates();
$this->checkedOutItems = $model->getCheckedOutItems();
$this->wafBlocks = $model->getRecentWafBlocks(5);
@@ -85,7 +85,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
<?php if (!empty($mokoExts)): ?>
<!-- Moko Component & Module Versions -->
<div class="d-flex flex-wrap gap-2 mb-4">
<div class="mokosuiteclient-ext-bar card mb-4">
<?php
$extIcons = [
'com_mokosuiteclient' => 'icon-cogs',
@@ -99,7 +99,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
$label = str_replace(['mod_mokosuiteclient_', 'com_mokosuiteclient'], ['', 'Component'], $ext->element);
$label = ucfirst($label ?: 'Component');
?>
<div class="d-flex align-items-center gap-2 px-3 py-2 rounded border bg-white" style="font-size:0.85rem;">
<div class="mokosuiteclient-ext-item">
<span class="<?php echo $icon; ?>" aria-hidden="true" style="color:#1a2744;"></span>
<span><?php echo $this->escape($label); ?></span>
<span class="badge bg-light text-dark"><?php echo $this->escape($ext->version); ?></span>
@@ -373,11 +373,25 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
<?php if (!empty($recentLogins)): ?>
<div class="table-responsive">
<table class="table table-sm table-striped mb-0">
<thead><tr><th>User</th><th>IP</th><th>Time</th></tr></thead>
<thead><tr><th>User</th><th>App</th><th>IP</th><th>Time</th></tr></thead>
<tbody>
<?php foreach ($recentLogins as $login): ?>
<?php foreach ($recentLogins as $login):
$msgData = json_decode($login->message ?? '{}');
$appKey = $msgData->app ?? '';
if (stripos($appKey, 'ADMINISTRATOR') !== false) {
$appLabel = 'Admin';
$appBadge = 'bg-dark';
} elseif (stripos($appKey, 'SITE') !== false) {
$appLabel = 'Site';
$appBadge = 'bg-info text-dark';
} else {
$appLabel = 'Unknown';
$appBadge = 'bg-secondary';
}
?>
<tr>
<td class="text-muted"><?php echo $this->escape($login->username ?? ''); ?></td>
<td><span class="badge <?php echo $appBadge; ?>" style="font-size:0.7rem;"><?php echo $appLabel; ?></span></td>
<td class="text-muted"><code><?php echo $this->escape($login->ip_address ?? ''); ?></code></td>
<td class="text-muted"><?php echo HTMLHelper::_('date', $login->log_date, Text::_('DATE_FORMAT_LC4')); ?></td>
</tr>
@@ -15,15 +15,14 @@
.mokosuiteclient-info-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
gap: 0.35rem;
flex: 0 1 auto;
min-width: 0;
}
.mokosuiteclient-info-label {
font-size: 0.7rem;
font-size: 0.75rem;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 0.04em;
@@ -40,6 +39,31 @@
padding: 0.3em 0.6em;
}
/* Moko extensions version bar — full-width auto-sized */
.mokosuiteclient-ext-bar {
display: flex;
flex-wrap: wrap;
gap: 0;
width: 100%;
}
.mokosuiteclient-ext-bar .mokosuiteclient-ext-item {
flex: 1 1 0;
min-width: 0;
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
padding: 0.5rem 0.75rem;
font-size: 0.82rem;
border-right: 1px solid #dee2e6;
white-space: nowrap;
}
.mokosuiteclient-ext-bar .mokosuiteclient-ext-item:last-child {
border-right: none;
}
/* Plugin cards */
.mokosuiteclient-plugin-card {
transition: box-shadow 0.15s ease, opacity 0.15s ease;