Nouvelle page /dashboard/repartition, renomme "Dashboard" en "SAS" dans le menu
Nouvelle page "Répartition/Soldes" entre "SAS" (ex-"Dashboard") et "Par compte" dans le menu : reprend "Solde par compte" et "Évolution du solde par compte", retirés du dashboard général pour le recentrer sur l'activité/CA/type/client. Mêmes données déjà exposées par /dashboard/api/stats (solde_par_compte, solde_par_compte_par_annee), aucun changement backend nécessaire pour cette page. js/dashboard-repartition.js reprend le HBarChart/MiniTrend de dashboard.js -- dupliqués plutôt que partagés, même convention que dashboard-compte.js. Racine Vue volontairement le même id #figli-dashboard-app que les deux autres pages dashboard (pas un id dédié) : dashboard.css scope ses variables CSS (thème clair/sombre) sur ce sélecteur, réutiliser le même id est comment les trois pages héritent du même thème sans feuille de style séparée -- vérifié en dark mode. Le lien de menu "Dashboard" devient "SAS" partout (les 3 templates Twig + le nav en render array de HistoryController, qui n'a pas de template Twig propre). dashboard.js : MiniTrend/soldeParCompteItems/comptesOrdonnes/ trendValues supprimés (code mort après le déplacement, plus rien ne les utilise sur le dashboard général).
This commit is contained in:
@@ -25,6 +25,16 @@ dashboard:
|
||||
- core/drupal
|
||||
- figli_compta_ledger/vue
|
||||
|
||||
dashboard_repartition:
|
||||
js:
|
||||
js/dashboard-repartition.js: {}
|
||||
css:
|
||||
theme:
|
||||
css/dashboard.css: {}
|
||||
dependencies:
|
||||
- core/drupal
|
||||
- figli_compta_ledger/vue
|
||||
|
||||
dashboard_compte:
|
||||
js:
|
||||
js/dashboard-compte.js: {}
|
||||
|
||||
@@ -481,6 +481,10 @@ function figli_compta_ledger_theme($existing, $type, $theme, $path) {
|
||||
'variables' => ['current_route' => NULL],
|
||||
'template' => 'figli-compta-dashboard',
|
||||
],
|
||||
'figli_compta_dashboard_repartition' => [
|
||||
'variables' => ['current_route' => NULL],
|
||||
'template' => 'figli-compta-dashboard-repartition',
|
||||
],
|
||||
'figli_compta_dashboard_compte' => [
|
||||
'variables' => ['current_route' => NULL],
|
||||
'template' => 'figli-compta-dashboard-compte',
|
||||
@@ -503,6 +507,7 @@ function figli_compta_ledger_page_attachments(array &$attachments) {
|
||||
$front_end_routes = [
|
||||
'figli_compta_ledger.home',
|
||||
'figli_compta_ledger.dashboard',
|
||||
'figli_compta_ledger.dashboard_repartition',
|
||||
'figli_compta_ledger.dashboard_compte',
|
||||
'figli_compta_ledger.history',
|
||||
'figli_compta_ledger.link_entree',
|
||||
|
||||
@@ -14,6 +14,14 @@ figli_compta_ledger.dashboard:
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
figli_compta_ledger.dashboard_repartition:
|
||||
path: '/dashboard/repartition'
|
||||
defaults:
|
||||
_controller: '\Drupal\figli_compta_ledger\Controller\DashboardController::repartitionView'
|
||||
_title: 'Répartition / Soldes - SAS Figures Libres'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
figli_compta_ledger.dashboard_compte:
|
||||
path: '/dashboard/compte'
|
||||
defaults:
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* @file
|
||||
* "Répartition / Soldes": solde par compte (all-time bar chart + one
|
||||
* year-by-year trend per compte), split out of the general /dashboard so
|
||||
* that page stays focused on activity/CA/type/client breakdowns. Same
|
||||
* /dashboard/api/stats endpoint as dashboard.js/dashboard-compte.js
|
||||
* (DashboardStatsController -- plain SQL GROUP BY, not Entity API), no
|
||||
* new backend needed -- solde_par_compte/solde_par_compte_par_annee were
|
||||
* already in that response, just unused on this page until now.
|
||||
*
|
||||
* HBarChart/MiniTrend are duplicated from dashboard.js rather than
|
||||
* shared, same established convention as dashboard-compte.js's own
|
||||
* copies (see dashboard.css's comment on the Signalement filter rules).
|
||||
* Root element id is deliberately the same #figli-dashboard-app as the
|
||||
* other two dashboard pages (not a unique id) -- dashboard.css scopes
|
||||
* its CSS custom properties (--figli-bg, dark-mode overrides, etc.) to
|
||||
* that selector, and reusing it is how all three dashboard pages pick
|
||||
* those up without a separate stylesheet.
|
||||
*/
|
||||
(function (Drupal, Vue) {
|
||||
'use strict';
|
||||
|
||||
const EUR_ROUND = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 });
|
||||
|
||||
async function fetchStats() {
|
||||
const res = await fetch('/dashboard/api/stats', { headers: { Accept: 'application/json' } });
|
||||
if (!res.ok) throw new Error('/dashboard/api/stats a répondu ' + res.status);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
// Horizontal bar chart -- same shape as dashboard.js's own copy,
|
||||
// including the zero-centered "diverging" layout for negative values
|
||||
// (a compte's solde can be a débit).
|
||||
const HBarChart = {
|
||||
props: {
|
||||
items: { type: Array, required: true },
|
||||
formatValue: { type: Function, required: true },
|
||||
colorFor: { type: Function, default: null },
|
||||
},
|
||||
computed: {
|
||||
hasNegative() {
|
||||
return this.items.some((i) => i.value < 0);
|
||||
},
|
||||
maxAbs() {
|
||||
return Math.max(1, ...this.items.map((i) => Math.abs(i.value)));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fillStyle(item) {
|
||||
const pct = (Math.abs(item.value) / this.maxAbs) * 100;
|
||||
if (this.hasNegative) {
|
||||
return item.value >= 0
|
||||
? { left: '50%', width: pct / 2 + '%' }
|
||||
: { right: '50%', width: pct / 2 + '%' };
|
||||
}
|
||||
return { left: 0, width: pct + '%' };
|
||||
},
|
||||
fillColor(item) {
|
||||
if (this.colorFor) return this.colorFor(item);
|
||||
return item.value < 0 ? 'var(--figli-error)' : 'var(--figli-positive)';
|
||||
},
|
||||
},
|
||||
template:
|
||||
'<div class="figli-hbar-chart">' +
|
||||
'<div class="figli-hbar-row" v-for="item in items" :key="item.label">' +
|
||||
'<div class="figli-hbar-label" :title="item.label">{{ item.label }}</div>' +
|
||||
'<div class="figli-hbar-track" :class="{\'is-diverging\': hasNegative}">' +
|
||||
'<div class="figli-hbar-zero" v-if="hasNegative"></div>' +
|
||||
'<div class="figli-hbar-fill" :style="[fillStyle(item), {background: fillColor(item)}]"></div>' +
|
||||
'</div>' +
|
||||
'<div class="figli-hbar-value">{{ formatValue(item.value) }}</div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
};
|
||||
|
||||
// Small multiples: one compact zero-centered bar-per-year trend per
|
||||
// compte, instead of a single 8-series line chart -- same reasoning
|
||||
// and same markup as dashboard.js's own copy.
|
||||
const MiniTrend = {
|
||||
props: {
|
||||
annees: { type: Array, required: true },
|
||||
values: { type: Array, required: true },
|
||||
formatValue: { type: Function, required: true },
|
||||
},
|
||||
computed: {
|
||||
maxAbs() {
|
||||
return Math.max(1, ...this.values.filter((v) => v !== null).map((v) => Math.abs(v)));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
barHeight(v) {
|
||||
if (v === null) return '0%';
|
||||
return Math.max(3, (Math.abs(v) / this.maxAbs) * 100) + '%';
|
||||
},
|
||||
},
|
||||
template:
|
||||
'<div class="figli-mini-trend">' +
|
||||
'<div class="figli-mini-bar-col" v-for="(v, i) in values" :key="annees[i]" :title="annees[i] + \' : \' + (v === null ? \'—\' : formatValue(v))">' +
|
||||
'<div class="figli-mini-bar-track">' +
|
||||
'<div class="figli-mini-bar-fill" :class="v !== null && v < 0 ? \'is-negative\' : \'is-positive\'" :style="{height: barHeight(v)}"></div>' +
|
||||
'</div>' +
|
||||
'<div class="figli-mini-bar-label">{{ annees[i].slice(2) }}</div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
};
|
||||
|
||||
const App = {
|
||||
components: { HBarChart, MiniTrend },
|
||||
data() {
|
||||
return { loading: true, error: null, stats: null };
|
||||
},
|
||||
computed: {
|
||||
soldeParCompteItems() {
|
||||
if (!this.stats) return [];
|
||||
return Object.entries(this.stats.solde_par_compte)
|
||||
.map(([label, value]) => ({ label, value }))
|
||||
.sort((a, b) => b.value - a.value);
|
||||
},
|
||||
// Comptes ordered by all-time solde (richest first) -- same order
|
||||
// as soldeParCompteItems, so the trend grid below reads as a
|
||||
// continuation of the bar chart above it rather than an unrelated
|
||||
// shuffle.
|
||||
comptesOrdonnes() {
|
||||
return this.soldeParCompteItems.map((i) => i.label);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatEurRound(v) {
|
||||
return EUR_ROUND.format(v);
|
||||
},
|
||||
trendValues(compte) {
|
||||
return this.stats.annees.map((y) => {
|
||||
const parAnnee = this.stats.solde_par_compte_par_annee[y];
|
||||
return parAnnee && parAnnee[compte] !== undefined ? parAnnee[compte] : null;
|
||||
});
|
||||
},
|
||||
async load() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
try {
|
||||
this.stats = await fetchStats();
|
||||
} catch (err) {
|
||||
this.error = err.message;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.load();
|
||||
},
|
||||
};
|
||||
|
||||
Drupal.behaviors.figliComptaDashboardRepartition = {
|
||||
attach(context) {
|
||||
const root = context.querySelector ? context.querySelector('#figli-dashboard-app') : null;
|
||||
if (root && !root.dataset.figliInitialized) {
|
||||
root.dataset.figliInitialized = '1';
|
||||
Vue.createApp(App).mount(root);
|
||||
}
|
||||
},
|
||||
};
|
||||
})(Drupal, Vue);
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* @file
|
||||
* Dashboard: charts and aggregate totals (solde par compte, chiffre
|
||||
* d'affaires par année, répartition par type, top clients), computed
|
||||
* server-side (DashboardStatsController -- plain SQL GROUP BY, not Entity
|
||||
* API) and rendered here as small dependency-free div/CSS bar charts. No
|
||||
* charting library: this project vendors its own JS (see js/vendor/), and
|
||||
* a handful of bar/line charts don't warrant pulling one in.
|
||||
* Dashboard: charts and aggregate totals (chiffre d'affaires par année,
|
||||
* répartition par type, top clients), computed server-side
|
||||
* (DashboardStatsController -- plain SQL GROUP BY, not Entity API) and
|
||||
* rendered here as small dependency-free div/CSS bar charts. No charting
|
||||
* library: this project vendors its own JS (see js/vendor/), and a
|
||||
* handful of bar/line charts don't warrant pulling one in. Solde par
|
||||
* compte lives on its own page (js/dashboard-repartition.js).
|
||||
*/
|
||||
(function (Drupal, Vue) {
|
||||
'use strict';
|
||||
@@ -125,41 +126,8 @@
|
||||
'</div>',
|
||||
};
|
||||
|
||||
// Small multiples: one compact zero-centered bar-per-year trend per
|
||||
// compte, instead of a single 8-series line chart -- eight overlapping
|
||||
// lines sharing one small area is hard to read; eight small independent
|
||||
// trends, each answering "is this person's balance growing or
|
||||
// shrinking", is not.
|
||||
const MiniTrend = {
|
||||
props: {
|
||||
annees: { type: Array, required: true },
|
||||
values: { type: Array, required: true },
|
||||
formatValue: { type: Function, required: true },
|
||||
},
|
||||
computed: {
|
||||
maxAbs() {
|
||||
return Math.max(1, ...this.values.filter((v) => v !== null).map((v) => Math.abs(v)));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
barHeight(v) {
|
||||
if (v === null) return '0%';
|
||||
return Math.max(3, (Math.abs(v) / this.maxAbs) * 100) + '%';
|
||||
},
|
||||
},
|
||||
template:
|
||||
'<div class="figli-mini-trend">' +
|
||||
'<div class="figli-mini-bar-col" v-for="(v, i) in values" :key="annees[i]" :title="annees[i] + \' : \' + (v === null ? \'—\' : formatValue(v))">' +
|
||||
'<div class="figli-mini-bar-track">' +
|
||||
'<div class="figli-mini-bar-fill" :class="v !== null && v < 0 ? \'is-negative\' : \'is-positive\'" :style="{height: barHeight(v)}"></div>' +
|
||||
'</div>' +
|
||||
'<div class="figli-mini-bar-label">{{ annees[i].slice(2) }}</div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
};
|
||||
|
||||
const App = {
|
||||
components: { HBarChart, ColumnChart: VBarChart, MiniTrend },
|
||||
components: { HBarChart, ColumnChart: VBarChart },
|
||||
data() {
|
||||
return { loading: true, error: null, stats: null };
|
||||
},
|
||||
@@ -168,12 +136,6 @@
|
||||
if (!this.stats) return [];
|
||||
return this.stats.annees.map((y) => ({ label: y, value: this.stats.ca_par_annee[y] || 0 }));
|
||||
},
|
||||
soldeParCompteItems() {
|
||||
if (!this.stats) return [];
|
||||
return Object.entries(this.stats.solde_par_compte)
|
||||
.map(([label, value]) => ({ label, value }))
|
||||
.sort((a, b) => b.value - a.value);
|
||||
},
|
||||
typeItems() {
|
||||
if (!this.stats) return [];
|
||||
return Object.entries(this.stats.total_par_type)
|
||||
@@ -214,13 +176,6 @@
|
||||
return { annee, items };
|
||||
}).filter((y) => y.items.length);
|
||||
},
|
||||
// Comptes ordered by all-time solde (richest first) -- same order
|
||||
// as soldeParCompteItems, so the trend grid below reads as a
|
||||
// continuation of the bar chart above it rather than an unrelated
|
||||
// shuffle.
|
||||
comptesOrdonnes() {
|
||||
return this.soldeParCompteItems.map((i) => i.label);
|
||||
},
|
||||
totalCA() {
|
||||
if (!this.stats) return 0;
|
||||
return Object.values(this.stats.ca_par_annee).reduce((a, b) => a + b, 0);
|
||||
@@ -242,12 +197,6 @@
|
||||
formatEurRound(v) {
|
||||
return EUR_ROUND.format(v);
|
||||
},
|
||||
trendValues(compte) {
|
||||
return this.stats.annees.map((y) => {
|
||||
const parAnnee = this.stats.solde_par_compte_par_annee[y];
|
||||
return parAnnee && parAnnee[compte] !== undefined ? parAnnee[compte] : null;
|
||||
});
|
||||
},
|
||||
typeColor(item) {
|
||||
return TYPE_COLORS[item.type] || '#6b7280';
|
||||
},
|
||||
|
||||
@@ -40,7 +40,23 @@ class DashboardController extends ControllerBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Third page: one compte associé (freelance) at a time -- entrées client
|
||||
* Third page: solde par compte, all-time and year by year -- split out
|
||||
* of the general /dashboard so that page stays focused on activité/CA/
|
||||
* type/client rather than per-compte balances. Same
|
||||
* /dashboard/api/stats data as /dashboard, just a different slice of it.
|
||||
*/
|
||||
public function repartitionView() {
|
||||
return [
|
||||
'#theme' => 'figli_compta_dashboard_repartition',
|
||||
'#current_route' => 'figli_compta_ledger.dashboard_repartition',
|
||||
'#attached' => [
|
||||
'library' => ['figli_compta_ledger/dashboard_repartition'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fourth page: one compte associé (freelance) at a time -- entrées client
|
||||
* vs versements freelance, and above all which entrées haven't been
|
||||
* (fully) paid out yet. Complements the aggregate /dashboard above,
|
||||
* which mixes every compte and every type together.
|
||||
|
||||
@@ -62,10 +62,10 @@ class HistoryController extends ControllerBase {
|
||||
// pinned next to the page title) -- this controller has no twig
|
||||
// template of its own to put a real <nav> in, but the CSS only
|
||||
// ever targets the class, not the tag, so a render-array
|
||||
// 'container' (<div>) here looks identical. None of the three
|
||||
// 'container' (<div>) here looks identical. None of the four
|
||||
// links is ever "active" here since this history feed isn't one
|
||||
// of them -- same as visiting it from any of the other pages'
|
||||
// nav, which doesn't include a 4th "Historique" entry either.
|
||||
// nav, which doesn't include a 5th "Historique" entry either.
|
||||
'nav' => [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['class' => ['figli-page-nav']],
|
||||
@@ -76,9 +76,14 @@ class HistoryController extends ControllerBase {
|
||||
],
|
||||
'dashboard' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Dashboard'),
|
||||
'#title' => $this->t('SAS'),
|
||||
'#url' => Url::fromRoute('figli_compta_ledger.dashboard'),
|
||||
],
|
||||
'dashboard_repartition' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Répartition/Soldes'),
|
||||
'#url' => Url::fromRoute('figli_compta_ledger.dashboard_repartition'),
|
||||
],
|
||||
'dashboard_compte' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Par compte'),
|
||||
|
||||
+2
-1
@@ -10,7 +10,8 @@
|
||||
#}
|
||||
<nav class="figli-page-nav">
|
||||
<a href="{{ path('figli_compta_ledger.home') }}" class="{{ current_route == 'figli_compta_ledger.home' ? 'is-active' : '' }}">Grand livre</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">Dashboard</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">SAS</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_repartition') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_repartition' ? 'is-active' : '' }}">Répartition/Soldes</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_compte') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_compte' ? 'is-active' : '' }}">Par compte</a>
|
||||
</nav>
|
||||
{% verbatim %}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{#
|
||||
Répartition / Soldes: solde par compte (all-time + évolution année par
|
||||
année), split out of the general dashboard (figli-compta-dashboard.html.twig)
|
||||
to keep that one focused on activité/CA/type/client.
|
||||
|
||||
{% verbatim %} below: this is Vue template syntax, not Twig -- both use
|
||||
{{ }}, so verbatim tells Twig to leave it alone and let Vue compile it
|
||||
in the browser.
|
||||
#}
|
||||
<nav class="figli-page-nav">
|
||||
<a href="{{ path('figli_compta_ledger.home') }}" class="{{ current_route == 'figli_compta_ledger.home' ? 'is-active' : '' }}">Grand livre</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">SAS</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_repartition') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_repartition' ? 'is-active' : '' }}">Répartition/Soldes</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_compte') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_compte' ? 'is-active' : '' }}">Par compte</a>
|
||||
</nav>
|
||||
{% verbatim %}
|
||||
<div id="figli-dashboard-app">
|
||||
<p v-if="loading">Chargement des données…</p>
|
||||
<p v-else-if="error" class="figli-error">Erreur de chargement du tableau de bord : {{ error }}</p>
|
||||
<template v-else>
|
||||
<section class="figli-chart-section">
|
||||
<h2>Solde par compte</h2>
|
||||
<p class="figli-note">Solde cumulé de chaque compte depuis l'origine (ouverture comprise) -- vert = créditeur, rouge = débiteur.</p>
|
||||
<h-bar-chart :items="soldeParCompteItems" :format-value="formatEurRound"></h-bar-chart>
|
||||
</section>
|
||||
|
||||
<section class="figli-chart-section">
|
||||
<h2>Évolution du solde par compte</h2>
|
||||
<p class="figli-note">Solde de clôture de chaque compte, année par année.</p>
|
||||
<div class="figli-trend-grid">
|
||||
<div class="figli-trend-card" v-for="compte in comptesOrdonnes" :key="compte">
|
||||
<div class="figli-trend-title">{{ compte }}</div>
|
||||
<mini-trend :annees="stats.annees" :values="trendValues(compte)" :format-value="formatEurRound"></mini-trend>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</div>
|
||||
{% endverbatim %}
|
||||
@@ -1,6 +1,7 @@
|
||||
{#
|
||||
Charts and aggregate totals: solde par compte, chiffre d'affaires par
|
||||
année, répartition par type, top clients. The spreadsheet-like
|
||||
Charts and aggregate totals: chiffre d'affaires par année, répartition
|
||||
par type, top clients. Solde par compte lives on its own page now (see
|
||||
figli-compta-dashboard-repartition.html.twig). The spreadsheet-like
|
||||
line-by-line view is the site's home page (figli-compta-home.html.twig).
|
||||
|
||||
{% verbatim %} below: this is Vue template syntax, not Twig -- both use
|
||||
@@ -9,7 +10,8 @@
|
||||
#}
|
||||
<nav class="figli-page-nav">
|
||||
<a href="{{ path('figli_compta_ledger.home') }}" class="{{ current_route == 'figli_compta_ledger.home' ? 'is-active' : '' }}">Grand livre</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">Dashboard</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">SAS</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_repartition') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_repartition' ? 'is-active' : '' }}">Répartition/Soldes</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_compte') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_compte' ? 'is-active' : '' }}">Par compte</a>
|
||||
</nav>
|
||||
{% verbatim %}
|
||||
@@ -34,23 +36,6 @@
|
||||
<column-chart :items="caParAnneeItems" :format-value="formatEurRound"></column-chart>
|
||||
</section>
|
||||
|
||||
<section class="figli-chart-section">
|
||||
<h2>Solde par compte</h2>
|
||||
<p class="figli-note">Solde cumulé de chaque compte depuis l'origine (ouverture comprise) -- vert = créditeur, rouge = débiteur.</p>
|
||||
<h-bar-chart :items="soldeParCompteItems" :format-value="formatEurRound"></h-bar-chart>
|
||||
</section>
|
||||
|
||||
<section class="figli-chart-section">
|
||||
<h2>Évolution du solde par compte</h2>
|
||||
<p class="figli-note">Solde de clôture de chaque compte, année par année.</p>
|
||||
<div class="figli-trend-grid">
|
||||
<div class="figli-trend-card" v-for="compte in comptesOrdonnes" :key="compte">
|
||||
<div class="figli-trend-title">{{ compte }}</div>
|
||||
<mini-trend :annees="stats.annees" :values="trendValues(compte)" :format-value="formatEurRound"></mini-trend>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="figli-chart-section">
|
||||
<h2>Répartition de l'activité par type</h2>
|
||||
<p class="figli-note">Montant total (HT, valeur absolue) par type de ligne, hors ouvertures.</p>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#}
|
||||
<nav class="figli-page-nav">
|
||||
<a href="{{ path('figli_compta_ledger.home') }}" class="{{ current_route == 'figli_compta_ledger.home' ? 'is-active' : '' }}">Grand livre</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">Dashboard</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">SAS</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_repartition') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_repartition' ? 'is-active' : '' }}">Répartition/Soldes</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_compte') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_compte' ? 'is-active' : '' }}">Par compte</a>
|
||||
</nav>
|
||||
{% verbatim %}
|
||||
|
||||
Reference in New Issue
Block a user