<?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-diagram-2-fill"></i> Master / Detail</h1>
<p class="lead">A <strong>master</strong> record (an order) with a grid of <strong>detail</strong> children (its line
items). This guide shows how to read and write the children from the master's page — two-way — and the small touches
like locking the child Add button. Each recipe says <strong>exactly where the code goes</strong>.</p>

<div class="help-callout help-callout-info">
    <i class="bi bi-diagram-3"></i>
    <div><strong>Set it up in the Designer.</strong> Drop a <em>Detail&nbsp;Grid</em> component on the master's
        Edit/View page and pick the <strong>child table + its foreign-key column</strong>. Then script it with
        <code>Runner.detail('&lt;child_table&gt;')</code> — its whole API is in the
        <a href="<?= $B ?>/help/PPjs">PP.js reference</a>.</div>
</div>

<div class="help-callout help-callout-info">
    <i class="bi bi-funnel"></i>
    <div><strong>Filter the child rows.</strong> A relation (Tables → Relation) has an optional
        <em>Filter&nbsp;(WHERE)</em> that scopes which children show under a master — on top of the foreign-key link.
        It supports <a href="<?= $B ?>/help/sql_variables">SQL variables</a>, e.g. <code>status = 'active'</code>,
        <code>created &gt;= :daysago.30</code>, or <code>assigned_to = :userid</code>.</div>
</div>

<h2>The API in one line</h2>
<pre><code>var items = Runner.detail('order_items');   // a grid object scoped to that child grid</code></pre>
<p>Or reach them all as a map keyed by child table (PHPRunner-style) — every value is a full grid object:</p>
<pre><code>var details = Runner.getDetailsPages();
details['order_items'].hideItem('add');       // hide that child grid's Add button
details['order_items'].getAllRecords();       // …read its rows
Runner.details().length;                       // how many detail grids on the page</code></pre>
<p><code>hideItem</code> / <code>showItem</code> / <code>disableItem</code> / <code>enableItem</code> are shortcuts
for <code>.button(name).hide()</code> etc. — names are <code>add</code>, <code>delete</code>, <code>edit</code>…</p>
<table class="help-table">
    <thead><tr><th>Call</th><th>Does</th></tr></thead>
    <tbody>
        <tr><td><code>items.getAllRecords()</code> / <code>getSelectedRecords()</code></td><td>All / ticked child rows.</td></tr>
        <tr><td><code>items.getCellValue(id, field)</code> / <code>getRow(id)</code></td><td>One child cell / row.</td></tr>
        <tr><td><code>items.setCellValue(id, field, value)</code></td><td>Write + persist one child cell.</td></tr>
        <tr><td><code>items.getRow(id).setValues({…})</code></td><td>Write several child cells, one save.</td></tr>
        <tr><td><code>items.editCell(id, field)</code></td><td>Open a child cell's inline editor for the user.</td></tr>
        <tr><td><code>items.button('add').hide()</code></td><td>Reach the child grid's own buttons.</td></tr>
        <tr><td><code>items.count()</code> / <code>Runner.details()</code></td><td>Row count / every detail grid on the page.</td></tr>
    </tbody>
</table>

<h2>Recipes</h2>

<h3>1 · Total the child lines onto the master</h3>
<?= pp_where('Events → <b>orders</b> → Client Events → <b>Before Form Submit</b>', 'js') ?>
<pre><code>var items = Runner.detail('order_items').getAllRecords();
Runner.setControlValue('total', items.reduce(function (s, it) { return s + (+it.qty) * (+it.price); }, 0));</code></pre>

<h3>2 · Require at least one child before saving</h3>
<?= pp_where('Events → <b>orders</b> → Client Events → <b>Before Form Submit</b>', 'js') ?>
<pre><code>if (Runner.detail('order_items').count() === 0) {
    pp.dialog.toast('warning', 'Add at least one line item.');
    return false;
}</code></pre>

<h3>3 · Write a computed value into every child row (two-way)</h3>
<?= pp_where('Designer → Custom Button (on the master) → <b>Client Before</b>', 'js') ?>
<pre><code>var d = Runner.detail('order_items');
for (var it of d.getAllRecords()) {
    await d.setCellValue(it.id, 'line_total', (+it.qty) * (+it.price));   // saves each child row
}
d.reload();
return false;</code></pre>

<h3>4 · Copy a master field into all children, then save</h3>
<?= pp_where('Designer → Custom Button (on the master) → <b>Client Before</b>', 'js') ?>
<pre><code>var currency = Runner.getControlValue('currency');
var d = Runner.detail('order_items');
for (var it of d.getAllRecords()) { await d.setCellValue(it.id, 'currency', currency); }
d.reload();
return false;</code></pre>

<h3>5 · Add a child, then jump the cursor into its first cell</h3>
<?= pp_where('Designer → Custom Button (on the master) → <b>Client After</b>', 'js') ?>
<pre><code>// after your Server stage inserts a child and returns $result['id']:
Runner.detail('order_items').editCell(result.id, 'sku');</code></pre>

<h3>6 · Lock the child Add button when the order is closed</h3>
<?= pp_where('Events → <b>orders</b> → Client Events → <b>On Page Init</b>', 'js') ?>
<pre><code>if (Runner.getControlValue('status') === 'closed') {
    Runner.detail('order_items').button('add').hide();
    Runner.hideButton('deleteSelected');
}</code></pre>

<h3>7 · Keep the master total correct on the server too</h3>
<p>Client totals are convenient, but recompute authoritatively on save so it's always right:</p>
<?= pp_where('Events → <b>orders</b> → Page Events → <b>After Edit Save</b>', 'php') ?>
<pre><code>$total = dbValue('SELECT COALESCE(SUM(qty*price),0) FROM order_items WHERE order_id = ?', [$id]);
dbExecute('UPDATE orders SET total = ? WHERE id = ?', [$total, $id]);</code></pre>

<h3>8 · Walk every detail grid on the page</h3>
<?= pp_where('Events → <b>orders</b> → Client Events → <b>On Page Init</b>', 'js') ?>
<pre><code>Runner.details().forEach(function (d) { console.log(d.table + ': ' + d.grid.count() + ' rows'); });</code></pre>

<div class="help-callout help-callout-warn">
    <i class="bi bi-shield-lock"></i>
    <div>Child writes go through the same permission-gated save endpoint as everything else — the user needs
        <strong>edit</strong> rights on the child table. Read the children anywhere; write where the grid is editable.</div>
</div>
