diff --git a/src/Controller/UserDashboardController.php b/src/Controller/UserDashboardController.php
index 214f34c..1da9785 100644
--- a/src/Controller/UserDashboardController.php
+++ b/src/Controller/UserDashboardController.php
@@ -3,20 +3,59 @@
namespace Origo\Controller;
use Origo\Entity\User;
+use Origo\Controller\ControllerBase;
use Origo\Controller\ControllerInterface;
use Origo\Services\Template;
use Origo\Services\Request;
-use Origo\Services\Renderer;
-class UserDashboardController implements ControllerInterface {
+class UserDashboardController extends ControllerBase implements ControllerInterface {
- /**
- * Get the response for the dashboard.
- */
public function getResponse(): string {
- $template = new Template('dashboard.html');
+ $user = new User();
+ $user->loadUserFromSession($this->request->getCookie('ORIGOSESS'));
+ $user_id = $user->get('id');
- return $template->render();
+ $query = $this->database->query("
+ SELECT pv.page, pv.title, pv.referrer, pv.ip, pv.language, pv.screen_res, pv.timestamp
+ FROM pageviews pv
+ JOIN user_accounts ua ON pv.account_id = ua.account_id
+ WHERE ua.user_id = :user_id
+ ORDER BY pv.timestamp DESC
+ ");
+ $query->bindParam(':user_id', $user_id);
+ $query->execute();
+ $results = $query->fetchAll();
+
+ $rows = '';
+ if ($results) {
+ foreach ($results as $row) {
+ $page = htmlspecialchars($row['page']);
+ $title = htmlspecialchars($row['title']);
+ $referrer = htmlspecialchars($row['referrer']);
+ $ip = htmlspecialchars($row['ip']);
+ $language = htmlspecialchars($row['language']);
+ $screen_res = htmlspecialchars($row['screen_res']);
+ $timestamp = htmlspecialchars($row['timestamp']);
+
+ $rows .= "
+
+ | {$title} |
+ {$page} |
+ {$referrer} |
+ {$ip} |
+ {$language} |
+ {$screen_res} |
+ {$timestamp} |
+
";
+ }
+ } else {
+ $rows = '| No pageviews yet. |
';
+ }
+
+ $template = new Template('dashboard.html');
+ return $template->render([
+ 'pageview_rows' => $rows,
+ ]);
}
}
diff --git a/templates/dash-body.html b/templates/dash-body.html
index aa5c124..e078636 100644
--- a/templates/dash-body.html
+++ b/templates/dash-body.html
@@ -5,9 +5,30 @@
Actions
+
+
+
Pageviews
+
+
+
+
+ | Title |
+ Page |
+ Referrer |
+ IP |
+ Language |
+ Screen |
+ Timestamp |
+
+
+
+ {{ pageview_rows }}
+
+
+
+
diff --git a/theme/partials/dashboard.scss b/theme/partials/dashboard.scss
index 7011248..6c0f8d7 100644
--- a/theme/partials/dashboard.scss
+++ b/theme/partials/dashboard.scss
@@ -1,8 +1,8 @@
#dashboard {
.dashboard-container {
- max-width: 800px;
+ max-width: 1200px;
margin: auto;
- padding-bottom: 100px;
+ padding: 0 24px 100px;
}
h1.page-title {
@@ -15,11 +15,71 @@
justify-content: flex-start;
list-style: none;
padding: 0;
-
li {
display: flex;
align-items: center;
gap: 1rem;
}
- }
+ }
+
+ #pageviews {
+ margin-top: 2rem;
+
+ h2 {
+ margin-bottom: 1rem;
+ color: var(--color-text-primary);
+ font-weight: var(--font-weight-bold);
+ }
+
+ .table-wrapper {
+ overflow-x: auto;
+ border-radius: 8px;
+ border: 1px solid var(--color-light-gray);
+ }
+
+ table.data-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: var(--font-size-small);
+ white-space: nowrap;
+
+ thead tr {
+ background: var(--color-primary);
+ color: var(--color-soft-cream);
+
+ th {
+ padding: 0.75rem 1rem;
+ text-align: left;
+ font-weight: var(--font-weight-bold);
+ }
+ }
+
+ tbody {
+ tr {
+ border-bottom: 1px solid var(--color-light-gray);
+ transition: background 0.15s ease;
+
+ &:nth-child(even) {
+ background: var(--color-light-gray);
+ }
+
+ &:nth-child(odd) {
+ background: var(--color-soft-cream);
+ }
+
+ &:hover {
+ background: var(--color-surface);
+ }
+
+ td {
+ padding: 0.65rem 1rem;
+ color: var(--color-text-primary);
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+ }
+ }
+ }
}
diff --git a/theme/style.css b/theme/style.css
index 7d46e2d..7dbf2bb 100644
--- a/theme/style.css
+++ b/theme/style.css
@@ -482,9 +482,9 @@ img {
}
#dashboard .dashboard-container {
- max-width: 800px;
+ max-width: 1200px;
margin: auto;
- padding-bottom: 100px;
+ padding: 0 24px 100px;
}
#dashboard h1.page-title {
margin-bottom: 0;
@@ -501,6 +501,54 @@ img {
align-items: center;
gap: 1rem;
}
+#dashboard #pageviews {
+ margin-top: 2rem;
+}
+#dashboard #pageviews h2 {
+ margin-bottom: 1rem;
+ color: var(--color-text-primary);
+ font-weight: var(--font-weight-bold);
+}
+#dashboard #pageviews .table-wrapper {
+ overflow-x: auto;
+ border-radius: 8px;
+ border: 1px solid var(--color-light-gray);
+}
+#dashboard #pageviews table.data-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: var(--font-size-small);
+ white-space: nowrap;
+}
+#dashboard #pageviews table.data-table thead tr {
+ background: var(--color-primary);
+ color: var(--color-soft-cream);
+}
+#dashboard #pageviews table.data-table thead tr th {
+ padding: 0.75rem 1rem;
+ text-align: left;
+ font-weight: var(--font-weight-bold);
+}
+#dashboard #pageviews table.data-table tbody tr {
+ border-bottom: 1px solid var(--color-light-gray);
+ transition: background 0.15s ease;
+}
+#dashboard #pageviews table.data-table tbody tr:nth-child(even) {
+ background: var(--color-light-gray);
+}
+#dashboard #pageviews table.data-table tbody tr:nth-child(odd) {
+ background: var(--color-soft-cream);
+}
+#dashboard #pageviews table.data-table tbody tr:hover {
+ background: var(--color-surface);
+}
+#dashboard #pageviews table.data-table tbody tr td {
+ padding: 0.65rem 1rem;
+ color: var(--color-text-primary);
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
#task-list .container {
max-width: 800px;