| Campaign Name | Status | @@ -14,7 +14,7 @@
|---|---|
{{ row.name }}
diff --git a/assets/vue/components/dashboard/KpiGrid.vue b/assets/vue/components/dashboard/KpiGrid.vue
index 5adb984..4835163 100644
--- a/assets/vue/components/dashboard/KpiGrid.vue
+++ b/assets/vue/components/dashboard/KpiGrid.vue
@@ -10,40 +10,70 @@
diff --git a/assets/vue/components/dashboard/PerformanceChartCard.vue b/assets/vue/components/dashboard/PerformanceChartCard.vue
index 7266e23..4dd02a2 100644
--- a/assets/vue/components/dashboard/PerformanceChartCard.vue
+++ b/assets/vue/components/dashboard/PerformanceChartCard.vue
@@ -1,8 +1,8 @@
+ Campaign Performance@@ -10,15 +10,119 @@
+
diff --git a/assets/vue/components/dashboard/QuickActionsCard.vue b/assets/vue/components/dashboard/QuickActionsCard.vue
new file mode 100644
index 0000000..51e5419
--- /dev/null
+++ b/assets/vue/components/dashboard/QuickActionsCard.vue
@@ -0,0 +1,89 @@
+
+ + Quick Actions ++
-
@@ -28,13 +31,33 @@
import AdminLayout from '../layouts/AdminLayout.vue'
import KpiGrid from '../components/dashboard/KpiGrid.vue'
import PerformanceChartCard from '../components/dashboard/PerformanceChartCard.vue'
+import QuickActionsCard from '../components/dashboard/QuickActionsCard.vue'
import RecentCampaignsCard from '../components/dashboard/RecentCampaignsCard.vue'
-const chart = {
- labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
+const appElement = document.getElementById('vue-app')
+
+const parseDashboardStats = () => {
+ const raw = appElement?.dataset.dashboardStats
+ if (!raw) {
+ return {}
+ }
+
+ try {
+ return JSON.parse(raw)
+ } catch {
+ return {}
+ }
+}
+
+const dashboardStats = parseDashboardStats()
+
+const chart = dashboardStats.chart || {
+ labels: [],
series: [
- { name: 'Opens', data: [2500, 2200, 10000, 4000, 4500, 3800] },
- { name: 'Clicks', data: [4200, 3500, 3200, 3000, 3600, 4100] },
+ { name: 'Opens', data: [] },
+ { name: 'Clicks', data: [] },
],
}
+
+const recentCampaigns = dashboardStats.recent_campaigns || []
diff --git a/package.json b/package.json
index 66fdd7b..7b5b04a 100644
--- a/package.json
+++ b/package.json
@@ -20,8 +20,10 @@
},
"dependencies": {
"@tatevikgr/rest-api-client": "^1.0.2",
+ "apexcharts": "^5.10.4",
"vue": "^3.5.16",
- "vue-router": "4"
+ "vue-router": "4",
+ "vue3-apexcharts": "^1.11.1"
},
"scripts": {
"prepare": "husky install"
diff --git a/src/Controller/DashboardController.php b/src/Controller/DashboardController.php
index 6215d3a..8a92959 100755
--- a/src/Controller/DashboardController.php
+++ b/src/Controller/DashboardController.php
@@ -4,6 +4,7 @@
namespace PhpList\WebFrontend\Controller;
+use PhpList\RestApiClient\Endpoint\StatisticsClient;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -11,13 +12,64 @@
class DashboardController extends AbstractController
{
+ public function __construct(private readonly StatisticsClient $statisticsClient)
+ {
+ }
+
#[Route('/', name: 'home', methods: ['GET'])]
public function index(Request $request): Response
{
+ $stats = $this->statisticsClient->getDashboardStats();
+ $recentCampaigns = [];
+ foreach ($stats->recentCampaigns as $campaign) {
+ $recentCampaigns[] = [
+ 'name' => $campaign->name,
+ 'status' => $campaign->status,
+ 'date' => $campaign->date?->format('Y-m-d') ?? '',
+ 'openRate' => $campaign->openRate,
+ 'clickRate' => $campaign->clickRate,
+ ];
+ }
+
+ $chartLabels = [];
+ $chartOpens = [];
+ $chartClicks = [];
+ foreach ($stats->campaignPerformance as $point) {
+ $chartLabels[] = $point->date?->format('M d') ?? '';
+ $chartOpens[] = $point->opens;
+ $chartClicks[] = $point->clicks;
+ }
+
return $this->render('spa.html.twig', [
'page' => 'Dashboard',
'api_token' => $request->getSession()->get('auth_token'),
'api_base_url' => $this->getParameter('api_base_url'),
+ 'dashboard_stats' => [
+ 'total_subscribers' => [
+ 'value' => $stats->totalSubscribers->value,
+ 'change_vs_last_month' => $stats->totalSubscribers->changeVsLastMonth,
+ ],
+ 'active_campaigns' => [
+ 'value' => $stats->activeCampaigns->value,
+ 'change_vs_last_month' => $stats->activeCampaigns->changeVsLastMonth,
+ ],
+ 'open_rate' => [
+ 'value' => $stats->openRate->value,
+ 'change_vs_last_month' => $stats->openRate->changeVsLastMonth,
+ ],
+ 'bounce_rate' => [
+ 'value' => $stats->bounceRate->value,
+ 'change_vs_last_month' => $stats->bounceRate->changeVsLastMonth,
+ ],
+ 'recent_campaigns' => $recentCampaigns,
+ 'chart' => [
+ 'labels' => $chartLabels,
+ 'series' => [
+ ['name' => 'Opens', 'data' => $chartOpens],
+ ['name' => 'Clicks', 'data' => $chartClicks],
+ ],
+ ],
+ ],
]);
}
}
diff --git a/src/Security/SessionAuthenticator.php b/src/Security/SessionAuthenticator.php
index badce5a..dbdf738 100644
--- a/src/Security/SessionAuthenticator.php
+++ b/src/Security/SessionAuthenticator.php
@@ -34,6 +34,9 @@ public function supports(Request $request): ?bool
public function authenticate(Request $request): Passport
{
+ if (!$request->hasSession()) {
+ throw new AuthenticationException('Session is not available');
+ }
$session = $request->getSession();
if ($session->has('auth_token')) {
diff --git a/templates/spa.html.twig b/templates/spa.html.twig
index 668d4df..91e6c01 100644
--- a/templates/spa.html.twig
+++ b/templates/spa.html.twig
@@ -5,6 +5,7 @@
{% block body %}
+
+
+
-
+ data-api-base-url="{{ api_base_url|default('') }}"
+ data-dashboard-stats="{{ dashboard_stats|default({})|json_encode|e('html_attr') }}">
{% endblock %}
diff --git a/yarn.lock b/yarn.lock
index e7838df..c3bd267 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1531,6 +1531,11 @@ ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
+apexcharts@^5.10.4:
+ version "5.10.4"
+ resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-5.10.4.tgz#79c9a05ab40b069f33873a1859de6cb0882ccf0e"
+ integrity sha512-gt0VUqZ2+mr25ScbUcKZgJr96jKYm4vjOcxEWCEh/E5F4dWqhyo3dBhPRvNNnkKiWxkMd2cBwj3ZYH3rK39fkA==
+
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
@@ -3700,6 +3705,11 @@ vue-router@4:
dependencies:
"@vue/devtools-api" "^6.6.4"
+vue3-apexcharts@^1.11.1:
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/vue3-apexcharts/-/vue3-apexcharts-1.11.1.tgz#5a9f8a319ae196e0490bc5413ab68bb400685c55"
+ integrity sha512-MbN3vg8bMG19wc0Lm1HkeQvODgLm56DgpIxtNUO0xpf/JCzYWVGE4jzXp2JISzy2s3Kul1yOxNQUYsLvKQ5L9g==
+
vue@^3.5.16:
version "3.5.16"
resolved "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz"
|