Browser-based estimator for Rotaract club membership dues invoices (Rotary International), built for Rotaract South Asia MDIO (RSAMDIO).
Live site: https://dues.rsamdio.org/
Figures are estimates only. Official invoices come from Rotary (My Rotary). Dues are calculated as of January 1 of the selected invoice year.
| Item | Detail |
|---|---|
| App | Vanilla HTML, CSS, JavaScript (no npm app toolchain) |
| UI | Tailwind CSS (CDN) + styles.css / styles.min.css |
| Auth / data | Firebase Auth (Google + Anonymous for live /learn join) + Cloud Firestore + Realtime Database (asia-southeast1) |
| Firebase project | clubinvoicecalculator |
Web Worker (pdf-worker.js) with jsPDF + autoTable |
|
| Admin | admin.html (access gated by admins/{uid} docs; Workshop Hosts tab) |
| Learn | /learn/ worksheet + /learn/host.html live-session console |
- Member roster: add, edit, remove; join/leave dates; club base (Community-Based $8 / University-Based $5)
- Live dues math: annual + prorated breakdowns, tax %, USD→local rate
- Bulk import: Excel/CSV upload, or public Google Sheets link (Anyone with the link can view)
- PDF report: Generate Detailed Report downloads a full estimate with roster breakdown
- Cloud backup (optional): Google sign-in; manual Save to Cloud only (nothing autosaves). Button shows Unsaved / Saving / Saved while signed in
- Sign-in conflict: if cloud data exists, choose current session or load from cloud
- First-visit help: instructions modal matching the current UI flow
- Rotaract Club Invoice Basics: eight-stage worksheet at
/learn/(self-paced on-device, or live session via a 6-digit host code). Same $8 / $5 January 1 model as the calculator.
ClubInvoiceCalculator/
├── index.html # Main calculator
├── admin.html # Admin dashboard + Workshop Hosts
├── app.js / app.min.js # Core app logic
├── learn/ # Rotaract Club Invoice Basics worksheet + host console
├── faq.html / how-ri-dues-work.html
├── llms.txt / sitemap.xml / robots.txt
├── modules/
│ ├── calculations.js # Dues math (also mirrored in app.js)
│ └── security.js # Validation / sanitization (also mirrored in app.js)
├── pdf-worker.js # PDF generation worker
├── firebase-config.js # Client Firebase config (includes RTDB URL)
├── firebase.json # Firestore + RTDB + Functions
├── database.rules.json # Live-session RTDB rules
├── firestore.rules # users, admins, workshopHosts
├── styles.css # Custom styles
├── build_minify.py # Generates *.min.css / *.min.js
├── firestore.indexes.json # Composite indexes for admin queries
├── vendor/ # Local libs (jspdf, xlsx, papaparse, autotable)
├── privacy.html / terms.html
├── fc66e32136887908af4a8222ec18b665.txt # IndexNow verification key
└── scripts/
└── submit-indexnow.mjs # Instant search engine submission (Bing, Yandex)
-
Clone
git clone https://github.com/rsamdio/ClubInvoiceCalculator.git cd ClubInvoiceCalculator -
Serve locally over HTTP
Do not rely onfile://. Use any static server so modules, workers, and Sheets import work:python3 -m http.server 8080
Then open
http://localhost:8080. -
Firebase (if using your own project)
Updatefirebase-config.js(keepdatabaseURLon the asia-southeast1 RTDB), enable Google Authentication, Anonymous Authentication (live/learnjoin), Firestore, and Realtime Database. Deploy rules from this repo:firebase deploy --only database,firestore:rules,functions --project clubinvoicecalculator
Note: The frontend is hosted on Netlify, so you do not need to deploy
hostingvia Firebase CLI. -
After editing JS or CSS
python3 build_minify.py
Regenerates
styles.min.css,app.min.js,modules/*.min.js, andpdf-worker.min.js.Note:
index.htmlmay mix minified and unminified script tags (for examplestyles.min.css+app.js). Confirm script/stylesheet references before a production deploy. -
Notify search engines (IndexNow) After deploying changes to Netlify, push updated URLs to Microsoft Bing, Yandex, etc.:
node scripts/submit-indexnow.mjs
- Fill Add New Member (name, club base, join date, optional leave date).
- Click Add Member. Totals update immediately.
- Download the CSV template, or make a copy of the Google Sheets template.
- Upload
.xlsx/.xls/.csv, or paste a public Sheets URL. - Review the preview, then confirm. Rows are appended to the current roster (not replaced).
- Set invoice year, tax %, and currency rate at the top.
- Review USD and local totals (including Annual + Prorated and tax lines).
- Click Generate Detailed Report for the PDF.
- Sign in from the top bar (optional for calculating).
- Click Save to Cloud to back up. Changes are not written until you save.
- On sign-in with existing cloud data, pick current session or load from cloud.
- Reset Roster clears the session roster after confirmation (does not by itself update the cloud).
- Open Rotaract Club Invoice Basics.
- Learn at my own pace keeps answers on the device. No Google account.
- Join a live session uses the 6-digit code from a host. Email is for follow-up only, not a login.
- Hosts sign in at
/learn/host.html. Calculator admins can host; other hosts are invited from Admin → Workshop Hosts. - The host console is two steps: all of your sessions, then that session’s classroom board. Other hosts cannot see your rooms.
{
id: string, // e.g. "member-{timestamp}"
name: string,
joinDate: "YYYY-MM-DD",
leaveDate: string, // optional, ""
memberType: "Community-Based" | "University-Based"
}{
taxPercentage: number, // default 18
currencyRate: number, // default 96
invoiceYear: string
}Logged when a PDF is generated for a signed-in user. Kept to the 10 most recent entries.
| Collection | Doc ID | Purpose |
|---|---|---|
users |
Firebase UID | Roster, settings, invoice summaries, profile fields |
admins |
Firebase UID | Admin access (admin.html); document existence = admin |
workshopHosts |
Firebase UID | Learn-session hosts (admins can host without this doc) |
Canonical rules live in firestore.rules and are deployed with:
firebase deploy --only firestore:rules --project clubinvoicecalculatorDo not deploy Pulse/RSAQA rules to this Firebase project. Bootstrap the first admin by creating admins/{uid} in the Console.
// users/{uid}: owner create/read/update; admin read/write/delete
// admins/{uid}: self-read for gate; admin create/update; delete others only (not self)
// workshopHosts/{uid}: self-get or admin; list/write admin onlyOlder README snippet is superseded by the committed firestore.rules file.
Canonical rules live in database.rules.json:
firebase deploy --only database --project clubinvoicecalculatorLive rooms live at rooms/{6-digit-code}. Each host only lists rooms under hostRooms/{theirUid}. Participants join with Anonymous Auth and can write only their own participant node while the room is open.
| Club base | Annual dues (USD) |
|---|---|
| Community-Based | $8 |
| University-Based | $5 |
Core function: calculateIndividualDue(joinDate, clubBase, invoiceYear, leaveDate).
Implemented in both modules/calculations.js and app.js. Keep them in sync when changing proration logic.
High-level behavior:
- Invoice year = January 1 of the selected year
- Join on 1 January of the invoice year → full-year Annual Dues on that invoice
- Join after 1 January of the invoice year → $0 on that invoice (Pro Rata Dues start the following year)
- Join on 1 January of the prior year → full-year Annual Dues only on the next invoice (no 12-month Pro Rata Dues)
- Mid-year leave after January 1 does not reduce the January 1 snapshot invoice
Firebase / cloud data
- Confirm Google Auth and Firestore are enabled
- Confirm you are signed in
- Remember: roster edits are local until Save to Cloud
Google Sheets import
- Share as Anyone with the link can view
- Use a
docs.google.com/spreadsheets/...URL - Or download CSV and use file upload
Scripts / PDF / bulk upload
- Serve over HTTP, not
file:// - Ensure
vendor/contains jspdf, autotable, xlsx, and papaparse - Hard-refresh after changing JS/CSS; run
python3 build_minify.pyif you rely on.minassets
Learn / live sessions
- Enable Anonymous Authentication for join-with-code
- Hosts must be a calculator admin or listed under Workshop Hosts
- Deploy RTDB rules (
database.rules.json) and keepdatabaseURLon asia-southeast1 /learn/host.htmlis not a public page (noindex)
Questions or feature requests: rsamdio@gmail.com
There is no separate open-source LICENSE file in this repository. Site use is covered by terms.html and privacy.html.