<h1>Notifications – <code>Notification</code> Class &amp; No-Code Rules</h1>
<p>
    The <strong>Notification</strong> class raises <strong>in-app notifications</strong> — the little
    messages that light up the <strong>bell</strong> in your generated app's navbar. It creates its own
    table, self-heals its schema, colour-codes messages by severity, and ships targeting helpers so you
    can notify one user, a role, a group, everyone, or the record's owner in a single line.
</p>
<p>
    There are <strong>two ways</strong> to send notifications:
</p>
<ol>
    <li><strong>No-code rules</strong> — the <em>Notifications</em> module (project sidebar 🔔). Point-and-click:
        “When a record is <em>added / edited / deleted</em>, notify <em>these people</em> with <em>this message</em>.”
        Compiled into event hooks at build time. <em>Recommended for most cases.</em></li>
    <li><strong>Code</strong> — call the <code>Notification</code> API from any Event (afterAdd / afterEdit / …)
        or custom page when you need full control.</li>
</ol>

<h2>Contents</h2>
<ul>
    <li><a href="#types">Severity types (colour &amp; icon)</a></li>
    <li><a href="#rules">No-code Notification Rules</a></li>
    <li><a href="#add">Sending from code — <code>Add()</code></a></li>
    <li><a href="#targeting">Targeting helpers (role / group / everyone / owner)</a></li>
    <li><a href="#read">Reading, counting &amp; marking read</a></li>
    <li><a href="#bell">The bell — badge, animation &amp; toast</a></li>
    <li><a href="#enable">Enabling the table</a></li>
    <li><a href="#examples">Real-world examples</a></li>
</ul>

<!-- =========================================== -->
<h2 id="types">Severity types</h2>
<p>
    Every notification has a <code>type</code>. It sets the icon and accent colour automatically — you never
    pick an icon unless you want to override it.
</p>
<table class="pp-help-table">
    <thead><tr><th><code>type</code></th><th>Auto icon</th><th>Accent</th><th>Use for</th></tr></thead>
    <tbody>
        <tr><td><code>info</code> <em>(default)</em></td><td><code>info-circle</code></td><td>Blue</td><td>General FYI, mentions, assignments</td></tr>
        <tr><td><code>success</code></td><td><code>check-circle</code></td><td>Green</td><td>Created / approved / completed</td></tr>
        <tr><td><code>warning</code></td><td><code>exclamation-triangle</code></td><td>Amber</td><td>Needs attention, nearing a limit</td></tr>
        <tr><td><code>error</code></td><td><code>x-circle</code></td><td>Red</td><td>Failed / rejected / overdue</td></tr>
    </tbody>
</table>
<p class="pp-help-note">
    Pass <code>'icon' =&gt; 'bi-cart'</code> to override the auto icon. Any Bootstrap-Icons name works.
</p>

<!-- =========================================== -->
<h2 id="rules">No-code Notification Rules</h2>
<p>
    Open the <strong>Notifications</strong> page in the project sidebar (🔔). Click a
    <strong>one-click template</strong> or <strong>New rule</strong>, then fill in four steps:
</p>
<table class="pp-help-table">
    <thead><tr><th>Step</th><th>Choose</th></tr></thead>
    <tbody>
        <tr><td><strong>1. When</strong></td><td>A table + trigger — <em>record added</em>, <em>edited</em>, or <em>deleted</em>.</td></tr>
        <tr><td><strong>2. Notify</strong></td><td>Audience — <em>Everyone</em>, a <em>Role</em>, a <em>Group</em>, specific <em>Users</em>, the <em>record owner</em>, or the <em>actor</em> (who did it).</td></tr>
        <tr><td><strong>3. Message</strong></td><td>Title + message + type. Insert live field values with <code>{field}</code> chips.</td></tr>
        <tr><td><strong>4. Link</strong></td><td>Where clicking the notification goes — the record, nothing, or a custom URL.</td></tr>
    </tbody>
</table>
<p>
    <strong>Placeholders:</strong> put any column of the saved row in curly braces and it is substituted at
    send time. For an <code>orders</code> rule:
</p>
<pre><code>Title:   New order #{id}
Message: {customer_name} placed an order for {total}</code></pre>
<p class="pp-help-note">
    Rules are stored per-project and <strong>compiled into the table's afterAdd / afterEdit / afterDelete
    event hooks at build time</strong> — no runtime overhead, and they sit alongside any hand-written event
    code (they never overwrite it). Enabling <em>any</em> rule automatically provisions the notification bell,
    its API endpoint, and the table in the generated app.
</p>

<!-- =========================================== -->
<h2 id="add">Sending from code — <code>Add()</code></h2>
<pre><code>$id = Notification::Add([
    'user_id' =&gt; 5,                       // recipient (null = everyone)
    'title'   =&gt; 'Orders',
    'message' =&gt; 'New order #1234 has been placed.',
    'type'    =&gt; 'success',                // info | success | warning | error
    'url'     =&gt; '/generated/orders/view.php?id=1234',
    'icon'    =&gt; 'bi-cart',                // optional — overrides the type icon
    'expire'  =&gt; '2026-12-31 23:59:59',    // optional auto-expiry
]);</code></pre>
<table class="pp-help-table">
    <thead><tr><th>Key</th><th>Meaning</th></tr></thead>
    <tbody>
        <tr><td><code>user_id</code></td><td>Recipient user id. <code>null</code> / omitted = broadcast to all. (<code>userId</code> also accepted.)</td></tr>
        <tr><td><code>title</code></td><td>Bold heading shown above the message.</td></tr>
        <tr><td><code>message</code></td><td>The notification body. <strong>Required.</strong></td></tr>
        <tr><td><code>type</code></td><td>Severity — drives colour + icon. Defaults to <code>info</code>.</td></tr>
        <tr><td><code>url</code></td><td>Where clicking navigates. (<code>link</code> also accepted.)</td></tr>
        <tr><td><code>icon</code></td><td>Override the auto icon (Bootstrap-Icons name).</td></tr>
        <tr><td><code>expire</code></td><td>Auto-hide after this timestamp.</td></tr>
    </tbody>
</table>
<p class="pp-help-note">
    You don't have to enable anything first — <code>Add()</code> creates the table on demand and
    <strong>self-heals its columns</strong>, so the rich fields (title / icon / url / type) work even if a
    leaner feature (e.g. the Calendar reminders) created the table earlier.
</p>

<!-- =========================================== -->
<h2 id="targeting">Targeting helpers</h2>
<p>Skip the “look up the user ids” boilerplate — these do it for you and return how many were sent:</p>
<table class="pp-help-table">
    <thead><tr><th>Helper</th><th>Sends to</th></tr></thead>
    <tbody>
        <tr><td><code>AddForUsers(array $ids, array $data)</code></td><td>Each id in the list (deduped).</td></tr>
        <tr><td><code>AddForRole(string $role, array $data)</code></td><td>Every user whose <code>role</code> matches.</td></tr>
        <tr><td><code>AddForGroup(int $groupId, array $data)</code></td><td>Every member of the group.</td></tr>
        <tr><td><code>AddForEveryone(array $data)</code></td><td>All users (one broadcast row).</td></tr>
        <tr><td><code>AddForRecordOwner(array $row, string $ownerField, array $data)</code></td><td>Whoever created the record (reads the id from <code>$ownerField</code>).</td></tr>
    </tbody>
</table>
<pre><code>// Alert every admin, green tick, links to the record
Notification::AddForRole('admin', [
    'title'   =&gt; 'New order',
    'message' =&gt; 'Order #' . $id . ' was placed',
    'type'    =&gt; 'success',
    'url'     =&gt; '/generated/orders/view.php?id=' . $id,
]);

// Tell the person who created the ticket it was answered
Notification::AddForRecordOwner($data, 'created_by', [
    'title'   =&gt; 'Ticket updated',
    'message' =&gt; 'Your ticket "' . $data['subject'] . '" was answered',
    'type'    =&gt; 'info',
]);

// Broadcast an announcement to everyone
Notification::AddForEveryone([
    'title'   =&gt; 'Maintenance tonight',
    'message' =&gt; 'The app will be read-only from 10–11pm.',
    'type'    =&gt; 'warning',
]);</code></pre>

<!-- =========================================== -->
<h2 id="read">Reading, counting &amp; marking read</h2>
<pre><code>// 20 most recent for the current user
$items  = Notification::Get(Security::CurrentUserData()['id'], 20);

// Only unread
$unread = Notification::Get($userId, 10, true);

// Unread badge count
$count  = Notification::Count($userId);

// Mark one / all read
Notification::MarkRead(12);
Notification::MarkAllRead($userId);

// Housekeeping — drop anything older than 60 days
Notification::DeleteOld(60);</code></pre>

<!-- =========================================== -->
<h2 id="bell">The bell — badge, animation &amp; toast</h2>
<p>
    The generated app's navbar bell is wired automatically whenever notifications are in use. It:
</p>
<ul>
    <li>Shows a <strong>red badge</strong> with the unread count.</li>
    <li><strong>Wiggles</strong> when a new message arrives (respects <em>reduced-motion</em>).</li>
    <li>Pops a <strong>toast</strong> for the newest unread message, colour-matched to its <code>type</code>.</li>
    <li>Polls every 30s and refreshes instantly when the tab regains focus.</li>
    <li>Colour-codes each row and shows an accent bar on unread items.</li>
</ul>
<p class="pp-help-note">
    Need to refresh the bell after your own AJAX action? Call <code>window.ppNotifyRefresh()</code> on the page.
</p>

<!-- =========================================== -->
<h2 id="enable">Enabling the table</h2>
<p>
    Usually you don't — a Notification <em>rule</em>, a <code>Notification::Add()</code> call, or the Calendar's
    in-app reminders each provision everything on their own. To turn it on explicitly, use
    <strong>Misc → Advanced → Notifications</strong> (set the table name, default <code>notifications</code>),
    or programmatically:
</p>
<pre><code>Notification::Enable('notifications');   // create + self-heal the table
Notification::SetTableName('notifications');</code></pre>

<!-- =========================================== -->
<h2 id="examples">Real-world examples</h2>

<h3>afterAdd — notify admins a customer signed up</h3>
<pre><code>Notification::AddForRole('admin', [
    'title'   =&gt; 'New customer',
    'message' =&gt; "'{$data['name']}' just registered",
    'type'    =&gt; 'success',
    'url'     =&gt; '/generated/customers/view.php?id=' . $id,
]);</code></pre>

<h3>afterEdit — warn the owner their order shipped</h3>
<pre><code>if (($data['status'] ?? '') === 'shipped') {
    Notification::AddForRecordOwner($data, 'created_by', [
        'title'   =&gt; 'Order shipped',
        'message' =&gt; 'Order #' . $id . ' is on its way',
        'type'    =&gt; 'info',
        'url'     =&gt; '/generated/orders/view.php?id=' . $id,
    ]);
}</code></pre>

<h3>Approaching a limit — a warning to managers</h3>
<pre><code>if ((int)$data['stock'] &lt; 5) {
    Notification::AddForRole('manager', [
        'title'   =&gt; 'Low stock',
        'message' =&gt; $data['name'] . ' is down to ' . $data['stock'],
        'type'    =&gt; 'warning',
    ]);
}</code></pre>

<hr>
<h3>🔗 Related Help Topics</h3>
<p>
    <a href="Email" class="btn btn-sm btn-outline-primary me-1">Email API</a>
    <a href="SMS" class="btn btn-sm btn-outline-primary me-1">SMS API</a>
    <a href="Calendar" class="btn btn-sm btn-outline-primary me-1">Calendar</a>
    <a href="Security" class="btn btn-sm btn-outline-primary">Security API</a>
</p>
