| Org Name | Type | Admin | Users | Registered | Binding |
|---|
| Organization | Type | Admin Email | Users | Work Hours | Device Binding | Registered |
|---|
| Name | Photo | Role | Staff ID | Dept | Device | 2FA | Bio |
|---|
| Staff Name | Organization | Date | First In | Last Out | Hours | Sessions | Shift | Status | Type |
|---|
| Photo | Name | Org | Role | Device Type | Browser | Device ID | Registered | Action |
|---|
| Org Code | Organization | Admin Email | Status | Plan | Expiry | Assigned By | Manage |
|---|
Free Trial is a special case: for an org's first 14 days from its actual registration date, the main app gives it every feature (the same as Gold) automatically, regardless of what's toggled on the Trial card below — that's what genuinely lets a new sign-up feel the full product. Only once those 14 days pass does the org drop to whatever is actually toggled on here. A per-org override under Organizations still applies at any time, including during that 14-day window.
Select an organization to see quick contact options.
No support notes yet
Announcements are stored in Firestore and shown as a banner in the Punctual app when users log in.
No announcements published
These run automatically whenever a new organization signs up, or when you assign / a staff member's plan is upgraded — no code required, powered by your own free Google Apps Script (GAS) relay(s).
Automatically capture every new organization signup to a Google Sheet in real time.
Sign ID · Timestamp · Full Name · Username · Email · Phone · Interested Plan · Status · Plan · UID
GAS setup script (quick copy-paste)
1. Create a Google Sheet, add the header row above.
2. Extensions → Apps Script, paste the script below, then Deploy → Web app (Execute as: Me, Access: Anyone).
3. Paste the resulting /exec URL above and save.
function doPost(e){
var sh = SpreadsheetApp.getActiveSheet();
var d = JSON.parse(e.postData.contents);
sh.appendRow([d.signId, d.timestamp, d.fullName, d.username,
d.email, d.phone, d.interestedPlan, d.status, d.plan, d.uid]);
return ContentService.createTextOutput('ok');
}
Receive instant WhatsApp alerts on new signups, plan assignments & upgrades.
☑ New org signup · ☑ Plan assigned · ☑ Plan switched · ☑ Plan upgraded
🔒 Your API key is stored in Firestore and is only ever read by this console — it's never exposed to end users.
Users receive emails on signup, approval, plan assignment and upgrade. Powered by your own webhook (GAS relay, EmailJS, etc.).
GAS Email Relay Script (quick setup)
1. Apps Script → New project, paste the script below.
2. Deploy → Web app (Execute as: Me, Access: Anyone).
3. Paste the resulting /exec URL above and save.
function doPost(e){
var d = JSON.parse(e.postData.contents);
MailApp.sendEmail({to:d.to, subject:d.subject, htmlBody:d.html});
return ContentService.createTextOutput('ok');
}
A quick reference for every page in this console. This is the super-admin (SaaS owner) console — it's separate from the Punctual app your organizations and their staff use, which has its own in-app Guide (Help → Guide) for admins and staff.
🏠 Overview
Platform-wide stats at a glance — total organizations, users, and how many are new this week. This is the first page you land on and reflects data across every organization on Punctual, not just one.
🏢 Organizations
Every organization signed up on Punctual. Click a row to open its detail modal — org name, plan, staff count, admin contact, and quick actions like suspending, archiving/restoring, or overriding individual feature flags for that one org (separate from its plan's default features).
The detail modal's ⚠️ Danger Zone panel (at the bottom) has a Delete Organization Permanently action, separate from Archive — Archive only hides an org and can be restored any time, while this irreversibly deletes the org document, every one of its users (Firestore record and their Firebase Authentication login), and all of its attendance, roster, requests, notifications, location, and message records. It requires typing the exact organization name to confirm, then a final confirm dialog, and calls the deleteOrgPermanently Cloud Function (needs firebase deploy --only functions to be deployed — without it, deletion is refused with a clear message rather than deleting only half the data). Use it only for a genuine removal request, never as routine cleanup.
The detail modal's 🔒 Anti-Abuse Controls panel has two safeguards, both settable only here — the org admin never sees or can change either: a max radius cap (500m by default, platform-wide) that org's own admin can't exceed even if they wanted to, with a full-lock option on top; and a lock on Device Binding so it can't be turned off, since that's what actually stops staff sharing one login to clock in for each other. Both exist for the same reason — an org admin (or one colluding with staff) could otherwise quietly loosen either setting to defeat what Punctual is for.
🛰️ Live Location Tracking is a separate, opt-in feature (Gold plan only, via the live_tracking flag under Feature Overrides below) — an org's own admin turns it on themselves in the main app's Settings, no lock needed, since unlike the two safeguards above it can only ever add visibility, never weaken one. While it's on, staff sharing their location during a Permission session always see a notice on their own clock screen — the main app never does this silently. A second, independent flag — live_tracking_map — controls whether that admin also sees a live, moving map (Uber/Bolt-style) for each person on Permission, on top of the plain coordinates/last-seen view; turning live_tracking off for an org disables tracking entirely regardless of this flag, but live_tracking_map can be toggled independently to give (or withhold) just the live-map view for orgs that already have tracking on.
👥 Users
Every user across every organization, with their role and which org they belong to. Click a user to view/edit their details directly — useful for support requests where a user is locked out or needs a field corrected. The detail view also shows whether a user has no personal phone (added by their org's admin for assisted clock in/out — see the main app's guide) and whether they have a login account at all.
Support Actions include Remove from team (soft-delete, restorable) and Delete Permanently (their Firestore record is deleted immediately; their Firebase Authentication login is also deleted, provided this project's Cloud Functions have been deployed — see functions/ in the deploy package and run firebase deploy --only functions once, on a Blaze-plan project. If functions aren't deployed, the Firestore record is still removed and you're told the Authentication login could not be).
🔑 Reset Password is for support requests where an org admin or staff member is locked out and Firebase's reset email isn't reaching their inbox — a known intermittent issue. It sets a brand-new password immediately via the resetUserPassword Cloud Function (same deploy requirement as above) and shows it to you once, so you can relay it to them directly; they should change it themselves from Security once signed back in. Works for anyone with a login, in any organization, including an org owner locked out of their own account — it's hidden for assisted-mode staff, who have no login to reset. Org admins and (if the admin has switched it on) co-admins can also do this themselves from the main app's Staff page, without needing to contact support at all — use this here mainly when the org's own admin is the one who's locked out, or is unavailable.
🕐 Attendance Monitor
A cross-organization view of attendance for a chosen period — useful for spotting an org with a lot of late arrivals or absences, or verifying an org's own reports match what's actually in Firestore. Requires the isSuperAdmin() Firestore rule to be present (see Settings → Firestore Rules Reminder) or attendance reads will come back empty. The exported PDF report opens with a written summary paragraph (total records, staff covered, late count, total hours, and permission sessions taken) above the table, and the table itself now has a Permission column showing how many permission sessions each row's staff member took — same on desktop and mobile. When viewing every organization at once, records are grouped under a collapsible header per organization instead of one long combined list — tap a header to fold or expand that org's rows.
The report opens in an overlay with two "✕ Close" buttons — one in the outer toolbar and one inside the report preview itself — both now close the overlay correctly (the inner one previously did nothing).
📱 Device Audit
Shows which device each user is bound to (one-device-per-account). Use this to help an org admin confirm a device reset actually took effect, or to spot suspicious multi-device activity. Each row now has its own ↺ Reset button so you can clear a user's device binding directly from here — use it when an org's own admin reaches out because their staff member changed phones and needs support resetting the device themselves. Staff are grouped under a collapsible header per organization instead of one long combined list — tap a header to fold or expand it.
💳 Subscribers & 🎫 Plans
Subscribers lists every org's current plan, status and expiry, with an "Assign Plan" action per row. Plans is where you check/uncheck exactly which features each plan tier (Trial/Basic/Lite/Gold) unlocks — this is the live source of truth the main app reads at runtime (via _planConfig), separate from the marketing copy on the sales page. When an org sends an upgrade request, it appears under Subscribers with an "Assign Plan" button pre-filled to what they asked for — review it, then Save Assignment. Doing this also fires the WhatsApp/email notification hooks configured under Integrations, if you've set those up.
🛟 Support CRM
A simple notes/ticket log per organization — use it to track support conversations, promises made, or issues you're following up on, so context isn't lost between sessions.
📢 Broadcast
Publish a platform announcement to all organizations or one specific org. These now appear inside every user's in-app Notification Inbox (the bell icon) rather than as a dismissible banner on their screen — so once published, staff and admins see it the next time they open their Inbox, and it stays there until they dismiss it individually.
A quick composer for messaging an org admin (or all org admins) directly via wa.me, with ready-made templates (welcome, update, support follow-up, reminder, billing). This opens WhatsApp Web/app on your own device — it's a manual outreach tool, distinct from the automated alerts configured under Integrations.
🔌 Integrations
Three automations, each optional and independent:
- Signup → Google Sheet — every new org signup is posted to a Google Sheet you control, via a Google Apps Script webhook you paste the URL for. Use "Test Connection" to send one test row, and "Sync All Users" to backfill your existing users into the sheet.
- Admin WhatsApp Notifications — you get a WhatsApp message on your own number whenever a new org signs up, or when you assign/upgrade a plan (fires from Subscribers/Plans → Assign Plan). Pick a provider (TextMeBot, CallMeBot, or Termii) and paste its API key.
- User Email Notifications — the org's own admin gets a welcome email on signup, sent through your own GAS relay/EmailJS webhook (a ready-made GAS script is included to copy-paste).
All three are best-effort — if a webhook is slow, unreachable, or simply not configured yet, the underlying signup/plan action still completes normally; nothing is ever blocked waiting on these.
🤝 Partners
Create a referral link for a reseller or agent (/?ref=CODE), read applications submitted from the website's Partners page (mark them handled or delete them). Each new application also emails support@afrisalespro.com and sends the applicant a confirmation, through the email relay set in Integrations → User Email Notifications (the card shows emails sent, 1 email sent or emails not sent; if the relay is not set up the application is still saved here). Deploy the partnerApply Cloud Function once for this to work, and see which organisations signed up under each code, grouped with their plan status. No commission is calculated here; terms are agreed with each partner individually.
⭐ Reviews & Feedback
Customers leave reviews and private feedback from Settings in the app (owners and co-admins, every plan). Choose a filter at the top: Reviews awaiting approval (new or edited reviews whose author allowed website use), Published, Hidden, Private reviews (the author did not allow website use, so they cannot be published) and Private feedback (ideas, problems and questions).
Approve & publish copies the review to the public sales page immediately (the page reads it live, so no redeploy is needed); Hide removes it from the website but keeps it here; Delete removes it everywhere. If a customer edits a published review, the old version stays live until you approve or hide the new one. Feedback has Reply by email, Mark handled and Delete. The sidebar badge counts reviews awaiting approval plus new feedback.
📱 Phone / WhatsApp numbers everywhere (v2.6.1)
Creating an account in the main app now asks for a Phone / WhatsApp number (required), stored on the user profile as phone. This feeds what this console already reads: the WhatsApp directory and the “all admins” broadcast now pick up every admin who signs up from v2.6.1 on — so you can call or WhatsApp an organisation to confirm its registration.
The organisation detail modal now shows an Admin Phone row (tap-to-call) plus call and WhatsApp buttons in its Actions row whenever the org’s admin has a number on file. In the main app, Staff Management has a new Phone column (after Name) with tap-to-call and a 💬 WhatsApp link, and the staff search matches numbers too. Existing staff add or update their own number from Profile > “📱 My phone / WhatsApp number”; admins edit theirs next to “Signed in as” in the sidebar or at the end of Settings. No Firestore rules change is needed for this release — deploy hosting only.
🗓️ Salaries by attendance, pay-type saving & hours for specific days (v2.5.100)
Salaries now follow attendance. Monthly, Weekly, Quarterly and Annual pay is shared over the run and multiplied by days present ÷ working days (working days = Settings > Work schedule days minus holidays; approved leave counts as present). The owner can switch to “full salary regardless of attendance” in Payroll > Pay rates (organizations.paySalaryMode = attendance/fixed, payLeavePaid). Staff “My pay” uses the same rules.
A pay type, unit name or currency can now be saved without typing a rate.
New plan gate day_schedules (Lite and Gold, under Scheduling & Policy): Settings > Work schedule > “Different hours on specific days” stores organizations.daySchedule ({"1":{start,end}}, 0 = Sunday). Lateness, early-clock-in block, reminders, forgot-to-clock-out and auto clock-out use that day's hours. Shifts keep their own times.
v2.6.0: each day can also have its own Late grace (mins) (daySchedule["1"].grace, blank = the organisation-wide grace), under the same day_schedules gate. The “Salaried staff” setting no longer overflows phone screens.
💱 Pay types, per-staff currency, payroll names, staff pay view & partner commission (v2.5.97)
Pay rates now support Monthly, Hourly, Daily, Weekly, Quarterly, Annually and Custom (rate × units typed per run), and each person can have their own currency (96 available). Mixed-currency runs show totals per currency and export per currency.
Payrolls can be named when saved and renamed later in History. Firestore rules now allow name and renamedAtMs updates on payrollRuns (deploy the rules).
New plan gate staff_pay_view (Lite and Gold): each person sees an estimate of their earnings from attendance, their pay type and rate under Profile (“My pay”). Updated in v2.5.98: this is now part of every staff account on those plans — the owner no longer has a switch (employees have a right to know what they are paid, as with payslips). Staff can read only their own pay profile. Pay rates also has “Currency for everyone” and the Pay rates / History panels fold away.
Partners: set default commission % / bonus / first-or-recurring at the top of the Partners tab, with per-code overrides. Record Amount paid now in Assign Plan to feed earnings. Terms and payments are stored in _partnerLedger, readable by super admins only.
🏦 Staff bank details, global bank files & payroll currencies (v2.5.96)
New plan gate staff_bank (Lite and Gold): staff, workers, trainees and teachers can add their own bank or mobile-money details from their Profile. They are saved in staffBank/<uid>, readable only by that person, the organisation's owner and super admins, and they appear in the owner's Payroll → Pay rates tab (whichever of the owner's and the staff member's entry was saved last is used). The owner-side bank column and the exports panel are shown when payroll_exports or staff_bank is on.
Under payroll_exports: 11 accounting-journal layouts (QuickBooks, Xero, Zoho, Sage 50, Sage Accounting, Odoo, NetSuite, Dynamics 365 Business Central, SAP Business One, MYOB, plain CSV) and 10 bank-file layouts (generic, Paystack, Flutterwave, SEPA, UK Bacs/Faster Payments, US ACH, South Africa EFT, Wise, SWIFT wire, mobile-money). Banks cover about 70 countries plus mobile-money wallets. Payroll now has a 96-currency selector (symbol, ISO code and decimals) that flows through screens, CSV, PDF, email, journals and bank files. Column layouts and bank codes were written from published templates and must be re-checked against each provider's current template; Nigerian NIP bank codes are built in, other countries' codes are entered by the owner.
Saved payroll runs are now soft-deleted: Delete moves a run to Recently deleted (History tab) and the owner can Restore it for 30 days; the owner's browser removes older ones. Runs show the date and time they were saved. Firestore rules were updated (deploy firestore:rules): payrollRuns may now be updated only to set or clear deletedAtMs/deletedBy, and the new staffBank collection is added (and is purged by Delete Permanently).
🔗 Integrations, exports & staff import (v2.5.95)
Three plan gates control these in Plans → features: payroll_exports (accounting journals for QuickBooks/Xero/Zoho/Sage, timesheet CSV, Naira bank payment file with the bank details saved in Pay rates) and staff_import (bulk staff CSV import) are on Lite and Gold; integrations (live Google Sheets sync and Zapier/Make webhooks) is Gold only. Staff CSV export follows the existing CSV export gate.
Integrations use three Cloud Functions that must be deployed (firebase deploy --only functions, Blaze plan): attendanceIntegrationEvents (clock-in, late, clock-out), absentSweep (runs every 15 minutes, sends absent events once a day after each organisation's cut-off time; it needs Cloud Scheduler, which Firebase enables on deploy) and integrationTest (the Send test buttons). Each organisation's webhook links live in orgIntegrations/<orgId>, readable only by that owner and by super admins. Only zapier.com, make.com, script.google.com and a few similar hosts are accepted. If an owner says nothing arrives, ask them to press Send test and read the status line under the Save button: it shows the last error (for example the Apps Script is not deployed with access set to Anyone).
Bank account numbers live in payProfiles (owner-only in the rules; super admins can read them for support, so treat them as confidential). Each bank-file download is logged in payoutExports without account numbers. Permanently deleting an organisation now also removes pay profiles, payroll runs, face-check evidence, bank-file logs and integration links.
⚙️ Settings
Console appearance (Light/Dark/System), your Firebase project credentials (stored locally in this browser only), your own super-admin status check, an editable display name (shown in the sidebar and anywhere your actions are attributed, e.g. "Assigned By"), and a copy-pasteable Firestore rules reminder for the isSuperAdmin() function this console's cross-org reads depend on.
This console is installable as its own app on desktop and mobile — look for an install/"Add to Home Screen" option in your browser, or use the "Install App" button in the sidebar once your browser offers it.
System follows your device's own light/dark setting automatically. Dark is the default look of this console.
Config is saved to localStorage. These are the same credentials as the Punctual app.
To grant super-admin access, set superAdmin: true
on your user document in Firestore:
users / {your-uid} → superAdmin = true
This is the name shown in the sidebar and anywhere your actions are attributed (e.g. "Assigned By").
Add these functions to your firestore.rules for full admin access:
function isSuperAdmin(){
return signedIn() &&
get(/databases/$(database)/documents
/users/$(request.auth.uid))
.data.superAdmin == true;
}
// Add to attendance + remoteRequests
// read rules: || isSuperAdmin()