diff --git a/CHANGELOG.md b/CHANGELOG.md index b5951c6..0fc219d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog ## [Unreleased] +### Added +- Dashboard: snapshot widget, backup trend chart (30 days), and storage breakdown by profile (#61) + ## [01.33.00] --- 2026-06-23 ## [01.33.00] --- 2026-06-23 diff --git a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini index 67fad73..41ac24e 100644 --- a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini @@ -33,6 +33,12 @@ COM_MOKOJOOMBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" COM_MOKOJOOMBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" COM_MOKOJOOMBACKUP_DASHBOARD_UPDATE_SITE="Update Site" COM_MOKOJOOMBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" +COM_MOKOJOOMBACKUP_DASHBOARD_SNAPSHOTS="Content Snapshots" +COM_MOKOJOOMBACKUP_DASHBOARD_VIEW_ALL="View All" +COM_MOKOJOOMBACKUP_DASHBOARD_LATEST_SNAPSHOT="Latest" +COM_MOKOJOOMBACKUP_DASHBOARD_NO_SNAPSHOTS="No snapshots yet. Create one from the Content Snapshots view." +COM_MOKOJOOMBACKUP_DASHBOARD_STORAGE_BREAKDOWN="Storage by Profile" +COM_MOKOJOOMBACKUP_DASHBOARD_BACKUP_TREND="Backup Trend (30 days)" ; Backups view COM_MOKOJOOMBACKUP_BACKUPS_TITLE="Backup Records" diff --git a/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php b/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php index 09e7bbe..b378f0a 100644 --- a/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php +++ b/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php @@ -198,6 +198,90 @@ class DashboardModel extends BaseDatabaseModel return false; } + /** + * Get latest snapshot info for the dashboard widget. + */ + public function getLatestSnapshot(): ?object + { + $db = $this->getDatabase(); + + try { + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__mokosuitebackup_snapshots')) + ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) + ->order($db->quoteName('created') . ' DESC'); + $db->setQuery($query, 0, 1); + + return $db->loadObject() ?: null; + } catch (\Throwable $e) { + return null; + } + } + + /** + * Get snapshot count. + */ + public function getSnapshotCount(): int + { + $db = $this->getDatabase(); + + try { + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName('#__mokosuitebackup_snapshots')); + $db->setQuery($query); + + return (int) $db->loadResult(); + } catch (\Throwable $e) { + return 0; + } + } + + /** + * Get backup size trend data for the last 30 days. + * Returns array of {date, total_size, count, status} grouped by day. + */ + public function getBackupTrend(): array + { + $db = $this->getDatabase(); + $cutoff = date('Y-m-d', strtotime('-30 days')); + + $query = $db->getQuery(true) + ->select('DATE(' . $db->quoteName('backupstart') . ') AS backup_date') + ->select('SUM(' . $db->quoteName('total_size') . ') AS day_size') + ->select('COUNT(*) AS day_count') + ->select('SUM(CASE WHEN ' . $db->quoteName('status') . ' = ' . $db->quote('fail') . ' THEN 1 ELSE 0 END) AS fail_count') + ->from($db->quoteName('#__mokosuitebackup_records')) + ->where('DATE(' . $db->quoteName('backupstart') . ') >= ' . $db->quote($cutoff)) + ->group('DATE(' . $db->quoteName('backupstart') . ')') + ->order('backup_date ASC'); + $db->setQuery($query); + + return $db->loadObjectList() ?: []; + } + + /** + * Get storage breakdown by profile. + */ + public function getStorageByProfile(): array + { + $db = $this->getDatabase(); + + $query = $db->getQuery(true) + ->select('p.title AS profile_title') + ->select('COUNT(*) AS backup_count') + ->select('COALESCE(SUM(r.total_size), 0) AS total_size') + ->from($db->quoteName('#__mokosuitebackup_records', 'r')) + ->join('LEFT', $db->quoteName('#__mokosuitebackup_profiles', 'p') . ' ON p.id = r.profile_id') + ->where($db->quoteName('r.status') . ' = ' . $db->quote('complete')) + ->group($db->quoteName('r.profile_id')) + ->order('total_size DESC'); + $db->setQuery($query); + + return $db->loadObjectList() ?: []; + } + /** * Get published backup profiles for the quick-action selector. * diff --git a/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php index 340dfcc..f5c9b88 100644 --- a/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php @@ -24,18 +24,26 @@ class HtmlView extends BaseHtmlView public array $systemHealth = []; public array $profiles = []; public bool $defaultDirWarning = false; + public ?object $latestSnapshot = null; + public int $snapshotCount = 0; + public array $backupTrend = []; + public array $storageByProfile = []; public function display($tpl = null): void { /** @var \Joomla\Component\MokoSuiteBackup\Administrator\Model\DashboardModel $model */ $model = $this->getModel(); - $this->lastBackup = $model->getLastBackup(); - $this->nextScheduled = $model->getNextScheduled(); - $this->stats = $model->getStats(); - $this->systemHealth = $model->getSystemHealth(); - $this->profiles = $model->getProfiles(); + $this->lastBackup = $model->getLastBackup(); + $this->nextScheduled = $model->getNextScheduled(); + $this->stats = $model->getStats(); + $this->systemHealth = $model->getSystemHealth(); + $this->profiles = $model->getProfiles(); $this->defaultDirWarning = $model->isUsingDefaultBackupDir(); + $this->latestSnapshot = $model->getLatestSnapshot(); + $this->snapshotCount = $model->getSnapshotCount(); + $this->backupTrend = $model->getBackupTrend(); + $this->storageByProfile = $model->getStorageByProfile(); $this->addToolbar(); diff --git a/source/packages/com_mokosuitebackup/tmpl/dashboard/default.php b/source/packages/com_mokosuitebackup/tmpl/dashboard/default.php index d82ac57..39e255d 100644 --- a/source/packages/com_mokosuitebackup/tmpl/dashboard/default.php +++ b/source/packages/com_mokosuitebackup/tmpl/dashboard/default.php @@ -109,6 +109,122 @@ document.querySelectorAll('.mb-tile').forEach(function(tile) { }); + +
+ : + escape($this->latestSnapshot->description); ?> +
++ latestSnapshot->created, Text::_('DATE_FORMAT_LC4')); ?> + — + + escape($type); ?> + +
++ + latestSnapshot->articles_count; ?> articles, + latestSnapshot->categories_count; ?> categories, + latestSnapshot->modules_count; ?> modules + — snapshotCount; ?> total snapshots + +
+ + + +