Purpose
The RBA Dev Team Portal is a private, invitation-only system for recruiting and managing volunteers who help develop and test the Texas RBA Case Management System. Volunteers are compensated with free or discounted access to the software based on their tier and duration of participation.
John manages all volunteers, assignments, and benefits manually from an Admin Dashboard. There is no self-service registration — every volunteer enters through an invitation code sent by John.
Scope
This document covers the Dev Team Portal only — the volunteer management and testing infrastructure. For the RBAMGR case management system itself, see the Build Plan and Implementation Plan.
Architecture & Stack
The portal runs on the same Duda + Firebase stack as RBAMGR, but as a completely separate Firebase project with its own database, authentication, and security rules.
- Frontend: Duda Business plan — HTML widgets, all pages hidden from navigation
- Database: Firebase Realtime Database — compat SDK only, NOT Firestore, NOT npm/ESM
- Auth: Firebase Authentication — Email/Password only
- Notifications: Brevo — volunteer invites, status change emails
- Admin claim:
Firebase custom claim
auth.token.admin === true— set via Node.js script
Duda Rules — Non-Negotiable
addEventListener()
only — never inline onclick=""
· !important
on all CSS declarations · Never use document.body.innerHTML =
· Firebase compat SDK via CDN script tags only · State changes via hidden/visible div toggling only
CDN Script Tags
Include all three compat SDK tags in every portal file that uses Firebase:
<script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-database-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-auth-compat.js"></script>
Firebase Configuration
Firebase project: rba-dev-team-portal
— completely separate from the RBAMGR project ( rba-mgr
).
Config Values
The full Firebase config object (apiKey, authDomain, databaseURL, projectId, storageBucket, messagingSenderId, appId) is in the project files. Retrieve it from the Firebase console under Project Settings → Your Apps.
Security Rules
{
"rules": {
"volunteers": {
"$volunteerId": {
".read": "auth != null && auth.uid === $volunteerId",
".write": "auth != null && auth.uid === $volunteerId"
}
},
"admin": {
".read": "auth != null && auth.token.admin === true",
".write": "auth != null && auth.token.admin === true"
},
"assignments": {
".read": "auth != null",
".write": "auth != null && auth.token.admin === true"
},
".read": false,
".write": false
}
}
Key rule logic: each volunteer can only read/write their own node. The admin
node requires the custom claim. Assignments are readable by any authenticated user but writable by admin only.
Brevo Email Configuration
Brevo handles all outbound email from the portal — volunteer invites, tier promotions, and assignment notifications. All sends use a direct fetch()
POST to the Brevo API — no CDN script tag required.
const BREVO_API_URL = 'https://api.brevo.com/v3/smtp/email';
async function sendBrevoEmail(toEmail, toName, subject, htmlContent) {
const response = await fetch(BREVO_API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'api-key': BREVO_API_KEY
},
body: JSON.stringify({
sender: { name: 'RBA Dev Team Portal', email: 'john@rbamgr.com' },
to: [{ email: toEmail, name: toName }],
subject: subject,
htmlContent: htmlContent
})
});
return response.ok;
}
Database Structure
The database has three top-level nodes: volunteers
, assignments
, and admin
. Volunteer data is keyed by Firebase Auth UID.
rba-dev-team-portal-default-rtdb/
├── volunteers/
│ └── [uid]/
│ ├── profile/
│ │ ├── name
│ │ ├── email
│ │ ├── joinedAt
│ │ └── invitedBy
│ ├── tier/
│ │ ├── level ← "bronze" | "silver" | "gold"
│ │ ├── assignedAt
│ │ └── assignedBy
│ ├── compensation/
│ │ ├── type ← "coupon" | "lifetime"
│ │ ├── couponCode ← populated for bronze/silver
│ │ ├── lifetimeFree ← true/false for gold
│ │ └── notes
│ └── submissions/
│ └── [submissionId]/
│ ├── type ← "feedback" | "bug"
│ ├── content
│ ├── severity
│ ├── component
│ └── submittedAt
├── assignments/
│ └── [assignmentId]/
│ ├── title
│ ├── description
│ ├── component ← which feature/form to review
│ ├── assignedTo ← [uid, uid, ...]
│ ├── dueDate
│ ├── status ← "active" | "closed"
│ └── createdAt
└── admin/
└── invites/
└── [inviteCode]/
├── email
├── tier
├── createdAt
└── used ← true/false
Volunteer Tiers
The portal uses three tiers. Tier advancement is always manual — John reviews contribution and promotes directly. No automatic tier changes occur.
Compensation Rules
Compensation is software access only — no revenue sharing or monetary payment at any tier. Coupon codes are generated and assigned manually by John for Bronze and Silver. The lifetime free flag is set manually in the admin dashboard for Gold.
Portal Components
Eight pages make up the portal. All are hidden from Duda navigation — access is through direct URL only.
Build Phases
The portal is built in three phases. Each phase builds on the previous.
- 1Auth + Admin FoundationAdmin Login (P1), Admin Dashboard shell (P2) — volunteer list, tier badges, invite button · Invite system — generate code, email via Brevo, mark used on first login · Volunteer Login (P3)
- 2Volunteer ExperienceVolunteer Home (P4) — tier status, compensation display, assignment list · Review Assignments (P5) · Feedback Form (P6) · Bug Report Form (P7)
- 3Admin Tools + Document VaultFull feedback and bug report viewer · Compensation manager (coupon codes, lifetime flag) · Tier promotion controls · Document Viewer (P8) — gated per tier
Firebase Setup Status
All Firebase prerequisites for the portal are complete.
-
Firebase project created (
rba-dev-team-portal) - Realtime Database enabled
- Security rules set (per spec above)
- Firebase Auth enabled (Email/Password)
- Admin user created in Firebase Auth
-
Admin custom claim set (
admin: true) via Node.js script - Web app registered, config retrieved
Troubleshooting
onclick
handlersaddEventListener()
!important
to all color declarationsdocument.body.innerHTML
was usedauth != null
check passing — user must be logged in