Skip to content

Commit

Permalink
Intègre le dashboard Metabase à la page stats
Browse files Browse the repository at this point in the history
  • Loading branch information
florimondmanca committed Oct 28, 2024
1 parent 822d340 commit 5ba0ee2
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ APP_MEL_ORG_ID=
SENTRY_DSN=
###< sentry/sentry-symfony ###

###> Metabase ###
APP_METABASE_SITE_URL=https://dialog-metabase.osc-fr1.scalingo.io
APP_METABASE_SECRET_KEY=
###< Metabase ###

###> symfony/lock ###
# Choose one of the stores below
# postgresql+advisory://db_user:db_password@localhost/db_name
Expand Down
1 change: 1 addition & 0 deletions assets/customElements/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ import './modal_trigger';
import './map';
import './map_form';
import './map_search_form';
import './responsive_iframe';
41 changes: 41 additions & 0 deletions assets/customElements/responsive_iframe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @ts-check

import { querySelectorOrError } from "./util";

customElements.define('d-responsive-iframe', class extends HTMLElement {
connectedCallback() {
/** @type {HTMLIFrameElement} */
const iframe = querySelectorOrError(this, 'iframe');

// Inspiré de : https://towardsdev.com/the-most-elegant-way-to-implement-dynamic-size-iframe-2-0-319b974f1048

const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
window.parent.postMessage({
type: 'resize',
width: entry.borderBoxSize[0].inlineSize,
});
}
});

resizeObserver.observe(document.body);

const maxWidth = +(this.getAttribute('maxWidth') || iframe.clientWidth);
const extraPadding = +(this.getAttribute('extraPadding') || '0'); // px

window.addEventListener('message', function (event) {
// Replace the localhost origin with either env variable or an actual domain

if (event.origin === 'http://localhost:8000') {
if (event.data.type === 'resize') {
const windowWidth = event.data.width;
const newWidth = Math.min(windowWidth - extraPadding, maxWidth);
try {
iframe.style.width = `${newWidth}px`;
}
catch { }
}
}
});
}
});
4 changes: 4 additions & 0 deletions assets/styles/utilities/dsfr-extensions/sizing.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
max-width: calc(37 * 0.5rem);
}

.fr-x-max-w-156w {
max-width: calc(156 * 0.5rem);
}

.fr-x-h-full {
height: 100%;
}
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.13",
"easycorp/easyadmin-bundle": "^4.7",
"firebase/php-jwt": "^6.10",
"jsor/doctrine-postgis": "^2.1",
"league/commonmark": "^2.4",
"martin-georgiev/postgresql-for-doctrine": "^2.6",
Expand Down
65 changes: 64 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ services:
$melCredentials: '%env(APP_MEL_LITTERALIS_CREDENTIALS)%' # format: 'user:pass'
$fougeresOrgId: '%env(APP_FOUGERES_ORG_ID)%'
$fougeresCredentials: '%env(APP_FOUGERES_LITTERALIS_CREDENTIALS)%' # format: 'user:pass'
$metabaseSiteUrl: '%env(APP_METABASE_SITE_URL)%'
$metabaseSecretKey: '%env(APP_METABASE_SECRET_KEY)%'

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
Expand Down
15 changes: 15 additions & 0 deletions src/Infrastructure/Adapter/JWTEncoder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Infrastructure\Adapter;

use Firebase\JWT\JWT;

final class JWTEncoder
{
public function encode(array $payload, string $secretKey): string
{
return JWT::encode($payload, $secretKey, 'HS256');
}
}
36 changes: 36 additions & 0 deletions src/Infrastructure/Adapter/MetabaseEmbedFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace App\Infrastructure\Adapter;

use App\Application\DateUtilsInterface;

final class MetabaseEmbedFactory
{
private const DASHBOARD_ID_STATS = 2;

public function __construct(
private string $metabaseSiteUrl,
private string $metabaseSecretKey,
private JWTEncoder $jwtEncoder,
private DateUtilsInterface $dateUtils,
) {
}

public function makeDashboardUrl(): string
{
// Ce code a été adapté du code d'intégration fourni par Metabase dans les options de partage d'une visualisation.
// Plus d'infos sur le "static embedding" : https://www.metabase.com/docs/latest/embedding/static-embedding

$payload = [
'resource' => ['dashboard' => self::DASHBOARD_ID_STATS],
'params' => (object) [], // sic: L'array doit être converti en 'object', voir : https://github.com/metabase/metabase/issues/6487#issuecomment-1535278810
'exp' => round($this->dateUtils->getNow()->getTimestamp()) + 10 * 60, // 10 minute expiration
];

$token = $this->jwtEncoder->encode($payload, $this->metabaseSecretKey);

return \sprintf('%s/embed/dashboard/%s#bordered=true&titled=true', $this->metabaseSiteUrl, $token);
}
}
5 changes: 5 additions & 0 deletions src/Infrastructure/Controller/StatisticsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use App\Application\QueryBusInterface;
use App\Application\Regulation\Query\GetStatisticsQuery;
use App\Infrastructure\Adapter\MetabaseEmbedFactory;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

Expand All @@ -14,6 +15,7 @@ final class StatisticsController
public function __construct(
private \Twig\Environment $twig,
private QueryBusInterface $queryBus,
private MetabaseEmbedFactory $metabaseEmbedFactory,
) {
}

Expand All @@ -22,8 +24,11 @@ public function __invoke(): Response
{
$statistics = $this->queryBus->handle(new GetStatisticsQuery());

$dashboardEmbedUrl = $this->metabaseEmbedFactory->makeDashboardUrl();

return new Response($this->twig->render('statistics.html.twig', [
'statistics' => $statistics,
'dashboardEmbedUrl' => $dashboardEmbedUrl,
]));
}
}
20 changes: 19 additions & 1 deletion templates/statistics.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,27 @@
{% endblock %}

{% block body %}
<div class="fr-container fr-py-2w fr-py-md-5w">
<div class="fr-container fr-x-max-w-156w fr-py-2w fr-py-md-5w">
<h1>{{ 'statistics.title'|trans }}</h1>

<div class="fr-grid-row fr-mb-3w">
<div class="fr-col">
<d-responsive-iframe
maxWidth="1200" {# Corresponds to 156w #}
extraPadding="32"
>
<iframe
src="{{ dashboardEmbedUrl }}"
frameborder="0"
width="1200"
height="600"
allowtransparency
title="{{ 'statistics.dashboard.title'|trans }}"
></iframe>
</d-responsive-iframe>
</div>
</div>

<div class="fr-grid-row fr-grid-row--gutters fr-grid-row--middle fr-mb-3w">
<div class="fr-col fr-col-12 fr-col-md-6">
<div class="fr-card">
Expand Down
4 changes: 4 additions & 0 deletions translations/messages.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -2040,6 +2040,10 @@
<source>statistics.title</source>
<target>Statistiques</target>
</trans-unit>
<trans-unit id="statistics.dashboard.title">
<source>statistics.dashboard.title</source>
<target>Tableau de bord propulsé par Metabase</target>
</trans-unit>
<trans-unit id="statistics.users">
<source>statistics.users</source>
<target>Nombre total d'utilisateurs</target>
Expand Down

0 comments on commit 5ba0ee2

Please sign in to comment.