Skip to content

Feature/phase 3b admin triage dispatch#45

Closed
Exc1D wants to merge 4 commits intomainfrom
feature/phase-3b-admin-triage-dispatch
Closed

Feature/phase 3b admin triage dispatch#45
Exc1D wants to merge 4 commits intomainfrom
feature/phase-3b-admin-triage-dispatch

Conversation

@Exc1D
Copy link
Copy Markdown
Owner

@Exc1D Exc1D commented Apr 19, 2026

Summary by Sourcery

Add admin tooling to dispatch responders and introduce a reusable rate-limiting service backed by Firestore.

New Features:

  • Provide an admin dispatch modal to assign an on-shift responder to a report from the desktop app.
  • Expose callable client helpers for verifying, rejecting, dispatching, and canceling dispatches for reports from the admin app.

Enhancements:

  • Introduce a shared Firestore-based rate-limiting service for backend operations.
  • Add a hook to surface eligible, on-shift responders per municipality by combining Firestore responder data with Realtime Database shift status.

Tests:

  • Add unit tests validating the behavior and limits of the Firestore-backed rate-limiting service.

Summary by CodeRabbit

  • New Features

    • Added dispatch modal for assigning responders to reports
    • Introduced rate limiting for API operations
    • Added callable functions for report verification, rejection, dispatch, and dispatch cancellation
  • Tests

    • Added rate limiting functionality tests
  • Chores

    • Updated responder app dependencies

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 19, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Added a responder dispatch system for the admin desktop application, including a hook to fetch eligible responders from Firestore and Realtime Database, a modal component for selecting and dispatching responders, and a service layer for Firebase callable functions. Additionally, implemented a Firestore-backed rate limiting service with accompanying tests, and added Firebase dependency to the responder app.

Changes

Cohort / File(s) Summary
Admin Desktop Responder Dispatch
apps/admin-desktop/src/hooks/useEligibleResponders.ts, apps/admin-desktop/src/pages/DispatchModal.tsx, apps/admin-desktop/src/services/callables.ts
New hook maintains dual state maps from Firestore (active responders) and Realtime Database (shift status), filtering and sorting eligible on-shift responders. Modal component dispatches selected responder with idempotency key. Service module exports typed callable wrappers for verifyReport, rejectReport, dispatchResponder, and cancelDispatch operations.
Rate Limiting Service
functions/src/services/rate-limit.ts, functions/src/__tests__/services/rate-limit.test.ts
New Firestore-backed rate limiting implementation using sliding window approach with timestamp filtering. Tracks requests per key and computes remaining capacity and retry timing. Test suite validates single-request and capacity-exhaustion scenarios using Firebase Rules Unit Testing environment.
Dependencies
apps/responder-app/package.json
Added Firebase SDK (^12.12.0) to responder app dependencies.

Sequence Diagram

sequenceDiagram
    participant Admin as Admin User
    participant Modal as DispatchModal
    participant Hook as useEligibleResponders
    participant Firestore as Firestore
    participant RTDB as Realtime DB
    participant Callable as dispatchResponder<br/>Callable
    participant Functions as Cloud<br/>Functions

    Admin->>Modal: Opens dispatch for reportId
    Modal->>Hook: useEligibleResponders(municipalityId)
    Hook->>Firestore: Query active responders
    Firestore-->>Hook: responder docs
    Hook->>RTDB: Listen /responder_index/{id}
    RTDB-->>Hook: shift status updates
    Hook-->>Modal: sorted eligible responders
    Modal->>Admin: displays responder list
    Admin->>Modal: selects responder & confirms
    Modal->>Modal: validate selection
    Modal->>Callable: dispatchResponder({reportId, responderUid, idempotencyKey})
    Callable->>Functions: invoke HTTPS callable
    Functions-->>Callable: {dispatchId, status, reportId}
    Callable-->>Modal: dispatch result
    Modal->>Admin: closes with confirmation
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Hopping through Firestore with delight,
Responders dispatched to the scene, oh so right!
Rate limits sliding like morning dew,
Idempotent keys ensure none slip through! ✨
The modal shines bright where heroes convene.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/phase 3b admin triage dispatch' directly describes the main changes: implementing admin dispatch functionality with eligibility checking and related infrastructure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/phase-3b-admin-triage-dispatch
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feature/phase-3b-admin-triage-dispatch

Comment @coderabbitai help to get the list of available commands and usage tips.

@Exc1D Exc1D closed this Apr 19, 2026
@Exc1D
Copy link
Copy Markdown
Owner Author

Exc1D commented Apr 19, 2026

Duplicated changes

@Exc1D Exc1D deleted the feature/phase-3b-admin-triage-dispatch branch April 19, 2026 01:13
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 19, 2026

Reviewer's Guide

Implements an admin dispatch workflow by adding a dispatch modal and responder-eligibility hook to the admin app, centralizing callable Cloud Function clients for report actions, introducing a Firestore-backed rate limiting service with tests, and wiring Firebase dependencies needed by the responder and admin apps.

Sequence diagram for admin dispatching a responder via the new modal

sequenceDiagram
  actor Admin
  participant AdminDesktopApp
  participant DispatchModal
  participant UseEligibleResponders
  participant Firestore
  participant RealtimeDatabase
  participant CallablesService
  participant FirebaseFunctions
  participant RateLimitService
  participant FirestoreRateLimits

  Admin->>AdminDesktopApp: Open report
  AdminDesktopApp->>DispatchModal: Render DispatchModal(reportId)

  DispatchModal->>UseEligibleResponders: useEligibleResponders(municipalityId)
  UseEligibleResponders->>Firestore: query responders by municipalityId and isActive
  Firestore-->>UseEligibleResponders: responder documents
  UseEligibleResponders->>RealtimeDatabase: subscribe /responder_index/municipalityId
  RealtimeDatabase-->>UseEligibleResponders: shift status by responder uid
  UseEligibleResponders-->>DispatchModal: eligible responders list
  DispatchModal-->>Admin: Show eligible responders

  Admin->>DispatchModal: Pick responder and click Confirm
  DispatchModal->>CallablesService: dispatchResponder(reportId, responderUid, idempotencyKey)
  CallablesService->>FirebaseFunctions: HTTPS callable dispatchResponder
  FirebaseFunctions->>RateLimitService: checkRateLimit(key, limit, windowSeconds, now)
  RateLimitService->>FirestoreRateLimits: transaction read/write rate_limits/key
  FirestoreRateLimits-->>RateLimitService: timestamps for key
  RateLimitService-->>FirebaseFunctions: allowed or rejected
  FirebaseFunctions-->>CallablesService: dispatch result
  CallablesService-->>DispatchModal: {status, dispatchId, reportId}
  DispatchModal->>Admin: Close modal on success or show error
Loading

ER diagram for the new rate_limits Firestore collection

erDiagram
  RATE_LIMITS {
    string key PK
    number[] timestamps
  }

  RESPONDERS {
    string id PK
    string municipalityId
    boolean isActive
    string displayName
    string agencyId
  }

  RATE_LIMITS ||--o{ RESPONDERS : throttles_actions_for
Loading

Class diagram for new dispatch UI, callables client, and rate limiting service

classDiagram
  class DispatchModal {
    +string reportId
    +function onClose()
    +function onError(msg)
    -string picked
    -boolean submitting
    +function confirm()
  }

  class UseEligibleRespondersHook {
    +function useEligibleResponders(municipalityId)
    -Record~string,EligibleResponder~ responders
    -Record~string,ShiftStatus~ shift
  }

  class EligibleResponder {
    +string uid
    +string displayName
    +string agencyId
  }

  class ShiftStatus {
    +boolean isOnShift
  }

  class CallablesService {
    +function verifyReport(reportId, idempotencyKey)
    +function rejectReport(reportId, reason, notes, idempotencyKey)
    +function dispatchResponder(reportId, responderUid, idempotencyKey)
    +function cancelDispatch(dispatchId, reason, idempotencyKey)
  }

  class RateLimitCheck {
    +string key
    +number limit
    +number windowSeconds
    +Timestamp now
  }

  class RateLimitResult {
    +boolean allowed
    +number remaining
    +number retryAfterSeconds
  }

  class RateLimitService {
    +function checkRateLimit(firestore, key, limit, windowSeconds, now) RateLimitResult
  }

  class FirestoreRespondersCollection {
    +string id
    +string municipalityId
    +boolean isActive
    +string displayName
    +string agencyId
  }

  class RealtimeResponderIndexNode {
    +string municipalityId
    +Record~string,ShiftStatus~ responderShiftIndex
  }

  DispatchModal --> UseEligibleRespondersHook : uses
  DispatchModal --> CallablesService : dispatchResponder
  UseEligibleRespondersHook --> EligibleResponder : returns
  UseEligibleRespondersHook --> ShiftStatus : filters by
  UseEligibleRespondersHook --> FirestoreRespondersCollection : queries
  UseEligibleRespondersHook --> RealtimeResponderIndexNode : subscribes
  RateLimitService --> RateLimitCheck : input
  RateLimitService --> RateLimitResult : output
Loading

File-Level Changes

Change Details Files
Add DispatchModal UI to allow admins to select and dispatch an eligible responder for a report.
  • Create a modal component that loads eligible responders for the admin’s municipality, tracks the selected responder, and calls a dispatch Cloud Function with an idempotency key on confirmation.
  • Handle loading/empty states for eligible responders and basic error handling on dispatch failure.
  • Wire confirm/cancel buttons with disabled state while a dispatch is in-flight.
apps/admin-desktop/src/pages/DispatchModal.tsx
Introduce a hook to compute eligible responders based on Firestore responder records and Realtime Database shift status.
  • Subscribe to Firestore responders collection filtered by municipality and active status and project to a simple EligibleResponder structure.
  • Subscribe to a Realtime Database index of responder shift status for the municipality and keep a local map of on-shift responders.
  • Derive and return a sorted list of responders that are both active and currently on shift.
apps/admin-desktop/src/hooks/useEligibleResponders.ts
Centralize callable Cloud Function clients for report verification, rejection, dispatching, and dispatch cancellation.
  • Define a shared IdempotencyKey type and helper methods wrapping httpsCallable for verifyReport and rejectReport with strong payload typing.
  • Add dispatchResponder callable that returns dispatch metadata and uses an idempotency key to ensure idempotent operations.
  • Add cancelDispatch callable with constrained reason values and typed response, returning function data directly.
apps/admin-desktop/src/services/callables.ts
Implement a Firestore-based generic rate limiting service and unit tests to enforce API operation limits.
  • Create checkRateLimit service that maintains a timestamp bucket per key in a rate_limits collection and enforces a sliding window limit within a Firestore transaction.
  • Compute remaining quota and retryAfterSeconds when the limit is exceeded, ensuring a minimum retry delay of one second.
  • Add Vitest tests using @firebase/rules-unit-testing to verify allowed/denied behavior, remaining count, and retryAfterSeconds for a fixed window.
  • Load Firestore security rules for the test environment and ensure isolation/reset between tests.
functions/src/services/rate-limit.ts
functions/src/__tests__/services/rate-limit.test.ts
Update responder app dependencies to include the Firebase JS SDK and lockfile adjustments.
  • Add firebase dependency to the responder app package.json to enable Firebase usage in the responder client.
  • Regenerate pnpm-lock.yaml to reflect the new dependency graph.
apps/responder-app/package.json
pnpm-lock.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The checkRateLimit implementation appends every timestamp to a single rate_limits document without pruning beyond the sliding window, which can cause the timestamps array and document size to grow unbounded over time; consider periodically compacting or capping the stored entries.
  • In DispatchModal, the bare <div role="dialog"> lacks any focus management or escape-key handling, so you may want to integrate it with your existing modal infrastructure or add basic focus trapping/keyboard dismissal for better accessibility and UX.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `checkRateLimit` implementation appends every timestamp to a single `rate_limits` document without pruning beyond the sliding window, which can cause the `timestamps` array and document size to grow unbounded over time; consider periodically compacting or capping the stored entries.
- In `DispatchModal`, the bare `<div role="dialog">` lacks any focus management or escape-key handling, so you may want to integrate it with your existing modal infrastructure or add basic focus trapping/keyboard dismissal for better accessibility and UX.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants