diff --git a/README.md b/README.md index 9cf3017a..03ef6db3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Most of these modules have been optimized and made compatible for PrestaShop v9. | Module name | Description | Minimum version | |----------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------| | [api_module](https://github.com/PrestaShop/example-modules/tree/master/api_module) | This example module demonstrates how to modify PrestaShop's new API. | v9.0.0 | -| [dashexample](https://github.com/PrestaShop/example-modules/tree/master/dashexample) | This module demonstrates how to integrate with the migrated Symfony dashboard page through its new dedicated hooks. | v9.2.0 | +| [dashexample](https://github.com/PrestaShop/example-modules/tree/master/dashexample) | This module demonstrates how to integrate with the migrated Symfony dashboard page through its new dedicated hooks, including charts rendered with the core-provided Chart.js. | v9.2.0 | | [demo_grid](https://github.com/PrestaShop/example-modules/tree/master/demo_grid) | This module demonstrates how to use Grid in PrestaShop | v9.0.0 | | [democonsolecommand](https://github.com/PrestaShop/example-modules/tree/master/democonsolecommand) | Example module showing how to implement a Symfony console command | v9.0.0 | | [democontrollertabs](https://github.com/PrestaShop/example-modules/tree/master/democontrollertabs) | Demo Creating modern Controllers and associate Tabs to them | v9.0.0 | diff --git a/dashexample/README.md b/dashexample/README.md index 74df2f2d..7b34916d 100644 --- a/dashexample/README.md +++ b/dashexample/README.md @@ -2,19 +2,21 @@ Demonstration module for the **migrated (Symfony) Back Office Dashboard** and its new dedicated hook family. -It is both living documentation of the integration contract and a validation vehicle: once installed with the `dashboard` feature flag enabled, it renders content in Zone One, Zone Two and the toolbar area of the new dashboard page. +It is both living documentation of the integration contract and a validation vehicle: once installed with the `dashboard` feature flag enabled, it renders content in every zone of the new dashboard page, including four Chart.js charts (doughnut, line, bar, polar area). ## What it demonstrates - Registering on the **new** dashboard hooks: `displayAdminDashboardZoneOne`, `displayAdminDashboardZoneTwo`, `displayAdminDashboardZoneThree`, `displayAdminDashboardTop`, `displayAdminDashboardBottom`, `displayAdminDashboardToolbar`. - Rendering hook content through **module Twig templates** (`views/templates/admin/*.html.twig`) — no Smarty, no `HelperForm`, no `Db::getInstance()`. - Passing and using hook **parameters** (`date_from` / `date_to`, the employee stats date range). -- Loading the module's **own CSS/JS assets from its hook output** (see `toolbar.html.twig`) — no `actionAdminControllerSetMedia`, no `get_class($this->context->controller)` detection. +- Rendering charts with the **core-provided Chart.js** and its **PrestaShop palette** (see below) — the module ships no charting library. +- Presenting each block as a **Bootstrap card** (`card` / `card-header` / `card-body`), the supported markup of the new Back Office theme. +- Loading the module's **own CSS/JS assets from its hook output** — no `actionAdminControllerSetMedia`, no `get_class($this->context->controller)` detection. Each hook template loads only what its own blocks need, so every hook keeps working when the others are unregistered. ## Requirements -- PrestaShop **9.2.0** or later (the version that introduces the migrated dashboard and its hooks). -- The **`dashboard` feature flag** enabled: *Advanced Parameters > New & Experimental Features > Dashboard page*. +- PrestaShop **9.2.0** or later (the version that introduces the migrated dashboard, its hooks and the core Chart.js bundle). +- The **`dashboard` feature flag** enabled: *Advanced Parameters > New & Experimental Features > Dashboard*. ## Install @@ -23,7 +25,21 @@ It is both living documentation of the integration contract and a validation veh php bin/console prestashop:module install dashexample ``` -Then enable the `dashboard` feature flag and open the Dashboard. You should see the module's blocks in the two zones and a marker in the toolbar area. +Then enable the `dashboard` feature flag and open the Dashboard. You should see the module's cards in every zone (four charts across the three columns) and a marker in the toolbar area. + +## Charts with the core-provided Chart.js + +The dashboard page loads **Chart.js v4** as an independent core bundle (`themes/new-theme/public/chartjs.bundle.js`) — only on that page, not in the main Back Office bundle. It exposes two globals: + +- **`Chart`** — the Chart.js entry point ([chartjs.org](https://www.chartjs.org)). +- **`psChart`** — the PrestaShop palette, mirroring the modern PrestaShop branding (the `.b-color-*` blocks of prestashop.com, one named color per `--color-N`), read from the design-kit tokens (`--cdk-*` CSS custom properties) where they exist: + - `psChart.colors` — named colors, in prestashop.com order: `white`, `black`, `blue`, `green`, `purple`, `yellow`, `lightGray`, `gray`, `paleGray`, `teal`, `lightBlue`, `midGray`, `offWhite`, `borderGray` (`teal` and `gray` are the darker accents to prefer for line strokes); + - `psChart.series` — the categorical ramp as an ordered array for multi-series charts: `green`, `teal`, `blue`, `yellow`, `gray`, `purple` (the brand pastels interleaved with the darker accents so adjacent series stay distinguishable for color-blind readers); + - `psChart.withAlpha(color, alpha)` — translucent variant of a color, e.g. for line-chart area fills. + +A chart whose datasets define **no color at all** is colored automatically with the PrestaShop palette (the core replaces the default Chart.js `colors` plugin) — see the Zone One doughnut. A chart that defines **any** color is left untouched, so pick every color from `psChart` explicitly when some series need a specific meaning — see the Zone Two line chart (previous period in neutral gray) and bar chart. + +Keep legends and tooltips enabled (Chart.js defaults) so series identity never relies on color alone, and give each `` a `role="img"` and an `aria-label` — canvases are invisible to screen readers. ## New vs legacy hook families @@ -82,10 +98,11 @@ This module registers **only the new hooks** on purpose, so it also serves as a | File | Role | |---|---| | `dashexample.php` | Module class: hook registration + hook callbacks rendering Twig | -| `views/templates/admin/zone_one.html.twig` | Zone One block | -| `views/templates/admin/zone_two.html.twig` | Zone Two block | -| `views/templates/admin/zone_three.html.twig` | Zone Three block | -| `views/templates/admin/top.html.twig` | Top area block | -| `views/templates/admin/bottom.html.twig` | Full-width bottom block | -| `views/templates/admin/toolbar.html.twig` | Toolbar block + asset loading | -| `views/css/dashexample.css`, `views/js/dashexample.js` | Module-owned assets | +| `views/templates/admin/zone_one.html.twig` | Zone One card: traffic-sources doughnut (auto-colored) | +| `views/templates/admin/zone_two.html.twig` | Zone Two cards: sales-trend line + monthly-goals bar (explicit palette tokens) | +| `views/templates/admin/zone_three.html.twig` | Zone Three card: orders-by-status polar area (auto-colored) | +| `views/templates/admin/top.html.twig` | Top area banner | +| `views/templates/admin/bottom.html.twig` | Full-width bottom card | +| `views/templates/admin/toolbar.html.twig` | Toolbar marker badge | +| `views/js/zone-one.js`, `views/js/zone-two.js`, `views/js/zone-three.js` | Per-zone chart initialization (each loaded by its own hook template) | +| `views/css/dashexample.css` | Module-owned styles | diff --git a/dashexample/dashexample.php b/dashexample/dashexample.php index 0de70ffa..a533c524 100644 --- a/dashexample/dashexample.php +++ b/dashexample/dashexample.php @@ -34,6 +34,13 @@ * architecture it is integrating with purely from which hook is called — no version detection, * no `get_class($controller)` check, no Smarty, no `HelperForm`, no `Db::getInstance()`. * + * Charts are rendered with the Chart.js library provided by the core: the dashboard page loads + * `chartjs.bundle.js`, which exposes the global `Chart` plus a `psChart` palette based on the + * PrestaShop design tokens — the module ships no charting library of its own. + * + * Each hook template loads the assets its own blocks need: hooks can be registered or + * unregistered independently, so no hook may depend on assets loaded by another one. + * * See README.md for how to stay compatible with the legacy dashboard at the same time. */ class DashExample extends Module @@ -49,7 +56,7 @@ public function __construct() parent::__construct(); $this->displayName = $this->l('Dashboard example'); - $this->description = $this->l('Demonstration of the new dedicated hooks of the migrated Symfony dashboard page.'); + $this->description = $this->l('Demonstration of the new dedicated hooks of the migrated Symfony dashboard page, with charts rendered by the core-provided Chart.js.'); } public function install(): bool @@ -68,35 +75,47 @@ public function install(): bool /** * Renders a block in the first (left) column of the Symfony dashboard. * Receives the employee date range selected on the page. + * + * Doughnut chart (traffic sources) with no colors of its own: the core palette + * plugin colors it automatically. */ public function hookDisplayAdminDashboardZoneOne(array $params): string { return $this->render('zone_one.html.twig', [ 'dateFrom' => $params['date_from'] ?? null, 'dateTo' => $params['date_to'] ?? null, + 'moduleUri' => $this->getPathUri(), ]); } /** * Renders a block in the second (center) column of the Symfony dashboard. + * + * Line chart with a previous-period overlay, and a goals-vs-actual bar chart, + * both using explicit colors picked from the core `psChart` palette. */ public function hookDisplayAdminDashboardZoneTwo(array $params): string { return $this->render('zone_two.html.twig', [ 'dateFrom' => $params['date_from'] ?? null, 'dateTo' => $params['date_to'] ?? null, + 'moduleUri' => $this->getPathUri(), ]); } /** * Renders a block in the third (right) column of the Symfony dashboard. * Receives the employee date range selected on the page. + * + * Polar area chart (orders by status) with no colors of its own: the core + * palette plugin colors it automatically, per data point like a doughnut. */ public function hookDisplayAdminDashboardZoneThree(array $params): string { return $this->render('zone_three.html.twig', [ 'dateFrom' => $params['date_from'] ?? null, 'dateTo' => $params['date_to'] ?? null, + 'moduleUri' => $this->getPathUri(), ]); } @@ -127,8 +146,9 @@ public function hookDisplayAdminDashboardBottom(array $params): string /** * Renders content in the toolbar area of the Symfony dashboard. * - * This hook is rendered once at the top of the page, so the module loads its own - * assets from here (via its hook output) instead of `actionAdminControllerSetMedia`. + * Like every other hook of this module, it only loads the assets its own block + * needs (via its hook output, not `actionAdminControllerSetMedia`), so the zones + * keep working if this hook is unregistered. */ public function hookDisplayAdminDashboardToolbar(array $params): string { diff --git a/dashexample/views/css/dashexample.css b/dashexample/views/css/dashexample.css index e51c5f68..69a47397 100644 --- a/dashexample/views/css/dashexample.css +++ b/dashexample/views/css/dashexample.css @@ -13,13 +13,11 @@ padding: 8px 14px; margin-bottom: 15px; border-radius: 4px; - background-color: #e6f0ff; - color: #25b9d7; + background-color: var(--cdk-blue-50, #e8edfd); + color: var(--cdk-blue-700, #2942cc); font-weight: 600; } -.dashexample-card .panel-heading { - display: flex; - align-items: center; - gap: 6px; +.dashexample-card canvas { + max-width: 100%; } diff --git a/dashexample/views/js/dashexample.js b/dashexample/views/js/dashexample.js deleted file mode 100644 index cc5fe884..00000000 --- a/dashexample/views/js/dashexample.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * This source file is subject to the Academic Free License 3.0 (AFL-3.0). - * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 - */ - -document.addEventListener('DOMContentLoaded', () => { - // The module manages its own assets: this file is loaded from the module's hook output - // (see views/templates/admin/toolbar.html.twig), not via actionAdminControllerSetMedia. - console.log('[dashexample] Loaded on the migrated Symfony dashboard page.'); -}); diff --git a/dashexample/views/js/zone-one.js b/dashexample/views/js/zone-one.js new file mode 100644 index 00000000..8ed034dc --- /dev/null +++ b/dashexample/views/js/zone-one.js @@ -0,0 +1,42 @@ +/** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + */ + +// Chart initialization for the Zone One block only: each hook template loads its own +// script, so the zones keep working when other hooks are unregistered. +// `Chart` is the global provided by the core chartjs.bundle.js on the dashboard page. +(function () { + function readJson(id) { + var el = document.getElementById(id); + return el ? JSON.parse(el.textContent) : null; + } + + function initDoughnut() { + var canvas = document.getElementById('dashexample-doughnut'); + var data = readJson('dashexample-doughnut-data'); + if (!canvas || !data || typeof Chart === 'undefined') { + return; + } + + // No colors set on purpose: the core psColors plugin applies the PrestaShop + // palette automatically to charts that do not define their own. + new Chart(canvas, { + type: 'doughnut', + data: { + labels: data.labels, + datasets: [{ + data: data.values, + }], + }, + options: { + plugins: { legend: { position: 'bottom' } }, + }, + }); + } + + document.addEventListener('DOMContentLoaded', initDoughnut); +})(); diff --git a/dashexample/views/js/zone-three.js b/dashexample/views/js/zone-three.js new file mode 100644 index 00000000..1be3bffe --- /dev/null +++ b/dashexample/views/js/zone-three.js @@ -0,0 +1,42 @@ +/** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + */ + +// Chart initialization for the Zone Three block only: each hook template loads its own +// script, so the zones keep working when other hooks are unregistered. +// `Chart` is the global provided by the core chartjs.bundle.js on the dashboard page. +(function () { + function readJson(id) { + var el = document.getElementById(id); + return el ? JSON.parse(el.textContent) : null; + } + + function initPolarArea() { + var canvas = document.getElementById('dashexample-polar'); + var data = readJson('dashexample-polar-data'); + if (!canvas || !data || typeof Chart === 'undefined') { + return; + } + + // No colors set on purpose: the core psColors plugin applies the PrestaShop + // palette automatically, per data point like a doughnut. + new Chart(canvas, { + type: 'polarArea', + data: { + labels: data.labels, + datasets: [{ + data: data.values, + }], + }, + options: { + plugins: { legend: { position: 'bottom' } }, + }, + }); + } + + document.addEventListener('DOMContentLoaded', initPolarArea); +})(); diff --git a/dashexample/views/js/zone-two.js b/dashexample/views/js/zone-two.js new file mode 100644 index 00000000..dfe30840 --- /dev/null +++ b/dashexample/views/js/zone-two.js @@ -0,0 +1,84 @@ +/** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + */ + +// Chart initialization for the Zone Two blocks only: each hook template loads its own +// script, so the zones keep working when other hooks are unregistered. +// `Chart` and `psChart` are the globals provided by the core chartjs.bundle.js on the +// dashboard page. Both charts pick explicit colors from the psChart palette (a chart +// that defines any color is left untouched by the core auto-coloring plugin). +(function () { + function readJson(id) { + var el = document.getElementById(id); + return el ? JSON.parse(el.textContent) : null; + } + + function initLine() { + var canvas = document.getElementById('dashexample-line'); + var data = readJson('dashexample-line-data'); + if (!canvas || !data || typeof Chart === 'undefined' || typeof psChart === 'undefined') { + return; + } + + new Chart(canvas, { + type: 'line', + data: { + labels: data.labels, + datasets: [ + { + label: 'Current period', + data: data.current, + borderColor: psChart.colors.teal, + backgroundColor: psChart.withAlpha(psChart.colors.teal, 0.15), + fill: true, + tension: 0.3, + }, + { + label: 'Previous period', + data: data.previous, + borderColor: psChart.colors.gray, + borderDash: [6, 4], + fill: false, + tension: 0.3, + }, + ], + }, + options: { + plugins: { legend: { position: 'bottom' } }, + scales: { y: { beginAtZero: true } }, + }, + }); + } + + function initBar() { + var canvas = document.getElementById('dashexample-bar'); + var data = readJson('dashexample-bar-data'); + if (!canvas || !data || typeof Chart === 'undefined' || typeof psChart === 'undefined') { + return; + } + + new Chart(canvas, { + type: 'bar', + data: { + labels: data.labels, + datasets: [ + { label: 'Goal (%)', data: data.goal, backgroundColor: psChart.colors.paleGray }, + { label: 'Actual (%)', data: data.actual, backgroundColor: psChart.colors.green }, + ], + }, + options: { + plugins: { legend: { position: 'bottom' } }, + scales: { y: { beginAtZero: true } }, + }, + }); + } + + document.addEventListener('DOMContentLoaded', function () { + initLine(); + initBar(); + }); +})(); diff --git a/dashexample/views/templates/admin/bottom.html.twig b/dashexample/views/templates/admin/bottom.html.twig index 564d7636..71c673fe 100644 --- a/dashexample/views/templates/admin/bottom.html.twig +++ b/dashexample/views/templates/admin/bottom.html.twig @@ -8,15 +8,17 @@ {% trans_default_domain 'Module.Dashexample.Admin' %} -
-

- view_stream - {{ 'Full-width bottom zone'|trans }} -

-
-

{{ 'This full-width block is rendered by the dashexample module through the displayAdminDashboardBottom hook.'|trans }}

-

- {{ 'Selected range: %from% → %to%'|trans({'%from%': dateFrom, '%to%': dateTo}) }} -

+
+
+

+ view_stream + {{ 'Full-width bottom zone'|trans }} +

+
+
+

{{ 'This full-width block is rendered by the dashexample module through the displayAdminDashboardBottom hook.'|trans }}

+
+
diff --git a/dashexample/views/templates/admin/toolbar.html.twig b/dashexample/views/templates/admin/toolbar.html.twig index 615042dd..ac5933d2 100644 --- a/dashexample/views/templates/admin/toolbar.html.twig +++ b/dashexample/views/templates/admin/toolbar.html.twig @@ -9,9 +9,9 @@ {% trans_default_domain 'Module.Dashexample.Admin' %} {# The module loads its own assets from its hook output — no actionAdminControllerSetMedia, - no get_class($controller) detection. #} + no get_class($controller) detection. Each hook template only loads what its own + blocks need (here the badge styling), so the hooks stay independent. #} -
extension diff --git a/dashexample/views/templates/admin/zone_one.html.twig b/dashexample/views/templates/admin/zone_one.html.twig index 8d5b854f..c95b70fb 100644 --- a/dashexample/views/templates/admin/zone_one.html.twig +++ b/dashexample/views/templates/admin/zone_one.html.twig @@ -8,15 +8,31 @@ {% trans_default_domain 'Module.Dashexample.Admin' %} -
-

- widgets - {{ 'Zone one'|trans }} -

-
-

{{ 'This block is rendered by the dashexample module through the displayAdminDashboardZoneOne hook.'|trans }}

-

- {{ 'Selected range: %from% → %to%'|trans({'%from%': dateFrom, '%to%': dateTo}) }} +{# Every hook template loads the assets its own blocks need, so each hook keeps + working when the others are unregistered. #} + + + +

+
+

+ pie_chart + {{ 'Traffic sources'|trans }} +

+
+
+

+ {{ 'Doughnut chart rendered with the core-provided Chart.js — no colors set, the PrestaShop palette is applied automatically.'|trans }}

+ + +
+
diff --git a/dashexample/views/templates/admin/zone_three.html.twig b/dashexample/views/templates/admin/zone_three.html.twig index 1b0666e0..41fde149 100644 --- a/dashexample/views/templates/admin/zone_three.html.twig +++ b/dashexample/views/templates/admin/zone_three.html.twig @@ -8,15 +8,31 @@ {% trans_default_domain 'Module.Dashexample.Admin' %} -
-

- bookmark - {{ 'Zone three'|trans }} -

-
-

{{ 'This block is rendered by the dashexample module through the displayAdminDashboardZoneThree hook.'|trans }}

-

- {{ 'Selected range: %from% → %to%'|trans({'%from%': dateFrom, '%to%': dateTo}) }} +{# Every hook template loads the assets its own blocks need, so each hook keeps + working when the others are unregistered. #} + + + +

+
+

+ data_usage + {{ 'Orders by status'|trans }} +

+
+
+

+ {{ 'Polar area chart — like the doughnut, no colors set, each data point picks the next palette color.'|trans }}

+ + +
+
diff --git a/dashexample/views/templates/admin/zone_two.html.twig b/dashexample/views/templates/admin/zone_two.html.twig index d18b6b80..cbcaef7b 100644 --- a/dashexample/views/templates/admin/zone_two.html.twig +++ b/dashexample/views/templates/admin/zone_two.html.twig @@ -8,17 +8,52 @@ {% trans_default_domain 'Module.Dashexample.Admin' %} -
-

- insights - {{ 'Zone two'|trans }} -

-
-

{{ 'This block is rendered by the dashexample module through the displayAdminDashboardZoneTwo hook.'|trans }}

-
    -
  • {{ 'Rendered with a module Twig template (no Smarty).'|trans }}
  • -
  • {{ 'No HelperForm, no Db::getInstance().'|trans }}
  • -
  • {{ 'Range received as hook parameters: %from% → %to%.'|trans({'%from%': dateFrom, '%to%': dateTo}) }}
  • -
+{# Every hook template loads the assets its own blocks need, so each hook keeps + working when the others are unregistered. #} + + + +
+
+

+ show_chart + {{ 'Sales trend'|trans }} +

+
+
+

+ {{ 'Line chart with a previous-period overlay, colored with explicit tokens from the core psChart palette.'|trans }} +

+ + +
+ +
+ +
+
+

+ bar_chart + {{ 'Monthly goals'|trans }} +

+
+
+

{{ 'Goals-vs-actual bar chart, also colored from the core psChart palette.'|trans }}

+ +