<?php
$B = htmlspecialchars(rtrim((string) ($baseUrl ?? ''), '/'));
if (!function_exists('pp_where')) {
    function pp_where(string $path, string $lang = 'php'): string {
        $l = $lang === 'js' ? 'JS' : 'PHP';
        return '<div class="help-where"><span class="help-where-tag"><i class="bi bi-geo-alt-fill"></i> Where</span>'
             . '<span class="help-where-path">' . $path . '</span>'
             . '<span class="help-where-lang ' . htmlspecialchars($lang) . '">' . $l . '</span></div>';
    }
}
?>
<h1><i class="bi bi-speedometer2"></i> Dashboard pages</h1>
<p class="lead">A dashboard is a page of <strong>widgets</strong> — KPI tiles, charts, mini-tables, activity feeds.
Most are <em>data-bound in the Designer</em> (no code), but you can also script them for live values, refreshes and
drill-downs. Each recipe says <strong>exactly where the code goes</strong>.</p>

<div class="help-callout help-callout-info">
    <i class="bi bi-magic"></i>
    <div><strong>Bind first, script second.</strong> Select a <em>Stat&nbsp;Card</em>, <em>KPI</em>, <em>Chart</em>,
        <em>Mini-Table</em> or <em>Recent Activity</em> widget in the Designer and pick a <strong>table + column +
        aggregate</strong> in its Properties — it fills itself from the database. Reach for code only when you need
        something the binding can't express.</div>
</div>

<h2>Ways to add data to a dashboard</h2>
<table class="help-table">
    <thead><tr><th>Want…</th><th>Do this</th></tr></thead>
    <tbody>
        <tr><td>A number, chart or table from one query</td><td>Designer → drop a KPI / Chart / Mini-Table widget → set table + aggregate.</td></tr>
        <tr><td>A custom / cross-table figure</td><td>A server action + a Custom Button or On-Page-Init fetch (below).</td></tr>
        <tr><td>Live values that refresh</td><td>On Page Init (JS) + <code>pp.run()</code> on a timer.</td></tr>
        <tr><td>Click a tile to open a filtered list</td><td>A Custom Button, or the Chart's built-in drill-down (Designer).</td></tr>
    </tbody>
</table>

<h2>Recipes</h2>

<h3>1 · A custom figure from a named server action</h3>
<?= pp_where('Custom Functions → PHP → <b>function action_openTickets($params)</b>', 'php') ?>
<pre><code>function action_openTickets($params) {
    return (int) dbValue("SELECT COUNT(*) FROM tickets WHERE status = 'open'");
}</code></pre>
<?= pp_where('Menu / Dashboard → Client Events → <b>On Page Init</b>', 'js') ?>
<pre><code>var n = await pp.run('openTickets', {});
Runner.getControl('open_tickets').setValue(n);   // a Stat Card / field bound by name</code></pre>

<h3>2 · Refresh a KPI every 30 seconds</h3>
<?= pp_where('Menu / Dashboard → Client Events → <b>On Page Init</b>', 'js') ?>
<pre><code>async function tick() {
    var n = await pp.run('openTickets', {});
    Runner.getControl('open_tickets').setValue(n);
}
tick();
setInterval(tick, 30000);</code></pre>

<h3>3 · A "Refresh now" Custom Button</h3>
<?= pp_where('Designer → Custom Button → <b>Client After</b>', 'js') ?>
<pre><code>Runner.reloadGrid();   // or location.reload() to refresh every widget</code></pre>

<h3>4 · Greet the signed-in user</h3>
<?= pp_where('Global Events → Application → <b>Before Display</b>', 'php') ?>
<pre><code>pp_set_proxy('welcome', 'Welcome back, ' . \PpPhp\Core\Security::CurrentUsername());</code></pre>
<?= pp_where('Menu / Dashboard → Client Events → <b>On Page Init</b>', 'js') ?>
<pre><code>var msg = pp.getProxy('welcome', '');
if (msg) pp.dialog.toast('info', msg);</code></pre>

<h3>5 · A KPI that colours itself on a threshold</h3>
<?= pp_where('Menu / Dashboard → Client Events → <b>On Page Init</b>', 'js') ?>
<pre><code>var overdue = +Runner.getControl('overdue').getValue() || 0;
if (overdue &gt; 0) Runner.getControl('overdue').addStyle('color:#dc2626;font-weight:800');</code></pre>

<h3>6 · Build a whole panel from a server action</h3>
<?= pp_where('Menu / Dashboard → Client Events → <b>On Page Init</b>', 'js') ?>
<pre><code>var rows = await pp.run('topProducts', { limit: 5 });   // returns [{name, sold}, …]
document.getElementById('topProducts').innerHTML =
    rows.map(function (r) { return '&lt;li&gt;' + pp.util.escapeHtml(r.name) + ' — ' + r.sold + '&lt;/li&gt;'; }).join('');</code></pre>

<div class="help-callout help-callout-info">
    <i class="bi bi-lightbulb"></i>
    <div>Chart/KPI binding, drill-down and the widget catalogue live in the Designer. For live data,
        <code>pp.run()</code> (client) + <code>action_&lt;name&gt;</code> (server) are the pair — see the
        <a href="<?= $B ?>/help/PPjs">PP.js</a> and <a href="<?= $B ?>/help/DB">DB</a> guides.</div>
</div>
