Driver Gamification
Points, badges, streaks, weekly challenges, and a rewards catalogue that turn safe driving into a game.
Driver Gamification
Driver Gamification is a behaviour layer on top of the base driver scorecard. Every trip earns or subtracts points; accumulating points unlocks badges, feeds a weekly leaderboard, and can be redeemed for rewards. Streaks reward consistency, weekly challenges reward specific behaviours, and the whole system is tenant-scoped — each account curates its own badges, rewards, and challenge cadence.
Concepts
| Concept | What it is | Model |
|---|---|---|
| Points | The unified currency. Earned from trips, badges, challenges, manual adjustments. Spent on rewards. | DriverPointsLedger — one row per credit / debit |
| Badges | Named achievements with criteria (streak days, total points, safe-driving distance, etc.). Awarding a badge adds bonus points. | DriverBadge + DriverBadgeAward |
| Streaks | Consecutive days / weeks of safe driving. The streak record tracks the current run and the all-time best. | DriverStreak |
| Weekly Challenges | Time-boxed goals with a comparison (e.g. "complete 20 safe trips this week"). Progress accumulates; completion pays out points. | DriverChallenge + DriverChallengeProgress |
| Rewards | Catalogue of things drivers can redeem with their point balance (e.g. gift cards, time off, merchandise). | DriverReward + DriverRewardRedemption |
| Leaderboard | Ranked list of drivers by total earned points for the tenant. | Computed from DriverPointsLedger |
Badges, challenges, and rewards are tenant-scoped — admins curate each account's set independently. Seeded predefined badges exist (see DriverBadge::PREDEFINED_KEYS) as a starter kit.
Points ledger
Every point event writes a ledger row. The ledger is append-only — adjustments are negative entries rather than edits — so the audit trail is complete.
Recognised source_type values:
| Source | When it fires |
|---|---|
scorecard | Trip completed — points derived from the scorecard's safety score |
badge | A badge's award bonus |
challenge | A weekly challenge's payout when completed |
redemption | Negative row when a reward is redeemed |
manual | Admin adjustment (with note) |
A driver's total points is always SUM(points) of their ledger rows.
Trip points
When a DriverScorecard lands for a completed trip, DriverGamificationService::awardTripPoints() computes a positive-only value based on the safety score tiers and idempotently writes a scorecard row keyed by (driver_id, source_type, source_id = scorecard.id). Running the same scorecard through twice doesn't double-pay.
Badges
A badge definition carries:
| Field | Purpose |
|---|---|
name, description, icon_url | Display |
criteria_type | One of streak_days, points_total, safe_distance_km, challenge_wins, perfect_week |
criteria_value | Threshold for the criteria type |
points_reward | Bonus added to the driver's ledger on award |
active | Soft-disable without deleting |
A driver can hold each badge at most once (DriverBadgeAward enforces uniqueness on (driver_id, badge_id)).
Streaks
A streak is a daily tally that resets if a driver skips a qualifying day. The service advances the current streak on a qualifying event, updates best_streak_days when exceeded, and lets downstream badge evaluations hook in.
Weekly challenges
DriverChallenge rows carry week_start / week_end, a description of the goal, a metric + comparison + target (e.g. metric=trips, comparison=>=, target=20), and a points_reward. One challenge can be active per tenant at a time.
Each driver's progress toward the active challenge is tracked in DriverChallengeProgress (current_value, completed_at). When current_value crosses target, the service marks it complete and writes a challenge ledger row.
Rewards
The rewards catalogue is a flat list per tenant: name, description, image_url, points_cost, stock (null = unlimited), active. A driver spends points by calling POST /api/{tenant}/drivers/rewards/{id}/redeem. The service:
- Verifies the reward is active and in stock.
- Verifies the driver's balance is at least
points_cost. - Writes a negative ledger row (
source_type = redemption,source_id = reward.id). - Decrements stock if finite.
- Creates a
DriverRewardRedemptionrecord for the admin to fulfil.
API surface
| Verb | Route | Purpose |
|---|---|---|
GET | /drivers/gamification/summary | Driver's own points, streak, badges, active challenge progress |
GET | /drivers/leaderboard | Top-N ranked drivers for the tenant |
GET | /drivers/gamification/challenge/active | Active challenge + driver's progress |
POST | /drivers/rewards/{id}/redeem | Spend points on a reward |
All live under the tenant prefix /api/{tenant}/… in the custom (non-generated) section of routes/api.php.
Permissions
- Any driver — can read their own summary, the leaderboard, and redeem rewards.
- Admin / Fleet Manager — additionally manage the badge / challenge / reward catalogues via the Nova admin and through the auto-generated CRUD endpoints.