Add broadcast notifications and transit route approvals
Broadcast: ride/firebase/send_fcm.php is an internal service guarded by a
shared secret, so the browser cannot call it — holding that key client-side
would expose it, and the endpoint cannot tell who the sender is. A new
Admin/notifications/broadcast.php sits in front of it: it runs behind
connect.php, requires super_admin, restricts the target to the two topics the
apps actually subscribe to ('drivers'/'passengers') so it cannot be used to
push to an arbitrary topic or a single device token, bounds the title and
body, writes an audit entry before dispatching, and only then forwards the
call internally with the shared secret.
The composer shows a live push preview and an explicit confirmation naming
the audience, since a broadcast cannot be recalled.
Route approvals: draft routes render with their stops, distance and stop
count, and approve/reject posts to transit/route/approve.php behind a
confirmation stating the consequence. Available to admins and super admins,
matching the endpoint's own role check.
Also render user-supplied text with unicode-bidi: plaintext — Arabic names,
addresses and messages were being laid out left-to-right inside the
English UI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
41a06bba0c
commit
a0ab6c5155
@@ -1253,3 +1253,76 @@ h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
.tariff-field .form-input { padding-left: 1rem; font-size: 0.9rem; }
|
||||
.tariff-field .form-input:disabled { opacity: 0.65; cursor: not-allowed; }
|
||||
|
||||
/* Route approvals */
|
||||
.stop-list {
|
||||
margin: 0;
|
||||
padding-left: 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.stop-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stop-list li span:first-child { color: var(--text-main); }
|
||||
|
||||
/* Broadcast preview */
|
||||
.push-preview {
|
||||
max-width: 420px;
|
||||
padding: 1rem 1.15rem;
|
||||
border-radius: var(--radius-md);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid var(--border-color);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.push-app {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
font-size: 0.72rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--text-subtle);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.push-app i { color: var(--primary); }
|
||||
|
||||
.push-title {
|
||||
font-weight: 600;
|
||||
color: var(--text-main);
|
||||
margin-bottom: 0.2rem;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.push-body {
|
||||
font-size: 0.86rem;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Bidirectional text: names, addresses and messages are often Arabic while the
|
||||
UI chrome is English. `plaintext` lets each value pick its own direction
|
||||
from its first strong character instead of inheriting the page's LTR. */
|
||||
.form-input,
|
||||
.data-table td,
|
||||
.push-title,
|
||||
.push-body,
|
||||
.kv-row strong,
|
||||
.stop-list li span:first-child,
|
||||
.kpi-tile-value {
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
|
||||
textarea.form-input { text-align: start; }
|
||||
|
||||
Reference in New Issue
Block a user