Skip to main content

Deputy Header Integration

Real-time on-shift staff count in the admin header, powered by Deputy API.


Overview

The header displays a live count of staff currently on shift, queried from Deputy's timesheet and roster data. This provides instant visibility into current staffing levels.

┌────────────────────────────────────────────────────────────┐
│ RABS: [Ticker Message] 👥? 👤3 💬0 ⛶ 📅 🔔 ⊞ 👤 ⚙️ │
│ ↑ │
│ On-shift count │
└────────────────────────────────────────────────────────────┘

Data Sources

Primary: Timesheets

Queries Deputy timesheets where:

  • StartTime >= today midnight
  • StartTime <= now
  • EndTime = 0 (indicates shift still active)

Fallback: Rosters

If no active timesheets found (e.g., staff don't clock in), falls back to:

  • Rosters where StartTime <= now AND EndTime >= now

API

Endpoint

GET /api/v1/staff/deputy/on-shift

Response

{
"success": true,
"data": {
"count": 3,
"timesheets": 3, // or "rosters": 5
"method": "timesheet", // or "roster"
"asOf": "2026-01-07T14:30:00.000Z"
}
}

Backend Implementation

Location

backend/services/deputy.js

Functions

// Primary function - tries timesheets first
async function getOnShiftCount() {
// Query timesheets with EndTime = 0
// Falls back to rosters if no results
}

// Fallback function
async function getOnShiftCountFromRosters() {
// Query rosters overlapping current time
}

Route

backend/routes_v1p/staff_deputy.js

router.get('/on-shift', async (req, res) => {
const result = await deputy.getOnShiftCount();
res.json({ success: true, data: result });
});

Frontend Implementation

Location

admin/src/js/shared/ticker.js

Functions

// Fetch current count
async function fetchStaffOnShift() {
const res = await fetch('/api/v1/staff/deputy/on-shift', {...});
return data.success ? data.data : null;
}

// Update DOM elements
function updateStaffCounter(data) {
const els = document.querySelectorAll('[data-staff-on-shift-count]');
els.forEach(el => {
el.textContent = String(data?.count ?? '—');
el.title = `As of ${new Date(data.asOf).toLocaleTimeString()}`;
});
}

// Initialize with 2-minute refresh
async function initStaffCounter() {
const data = await fetchStaffOnShift();
updateStaffCounter(data);
setInterval(async () => {
updateStaffCounter(await fetchStaffOnShift());
}, 120000); // 2 minutes
}

HTML Element

admin/src/html/partials/app-header.html

<a href="page_staff.html" class="menu-link" data-bs-toggle="tooltip" 
data-bs-placement="bottom" data-bs-title="Staff on shift">
<div class="menu-icon">
<i class="nav-icon bi-person-badge"></i>
<span class="ms-1" data-staff-on-shift-count></span>
</div>
</a>

Refresh Behavior

EventAction
Page loadImmediate fetch
Every 2 minutesBackground refresh
TooltipShows timestamp of last update

Error Handling

API Errors

  • Console warning logged
  • Counter shows (em dash)
  • Continues polling for recovery

No Active Shifts

  • Timesheet query returns 0 → automatically tries rosters
  • If both return 0 → displays 0 (legitimate result)

Deputy Not Configured

  • Endpoint returns 503
  • Counter shows

Tooltip Information

The counter tooltip indicates data source:

  • Timesheet method: As of 2:30:45 PM
  • Roster method: Based on roster (timesheets unavailable)

Performance

  • API Call: ~200-500ms (Deputy API latency)
  • Refresh: Every 2 minutes (conservative to avoid rate limits)
  • Caching: None currently (real-time data important)

Future Enhancements

  1. Click to expand - Show list of who's on shift
  2. Shift change notifications - Alert when someone clocks in/out
  3. Location breakdown - Count per site/department
  4. Historical graph - Staffing levels over time
  5. Cache layer - Reduce Deputy API calls with 30-60s cache