feat(activity): per-tenant audit feed in notification dropdown

activity_events records booking lifecycle transitions with the actor
behind each change — staff (dashboard), customer (self-service), or
system (auto-promotion). The notification dropdown leads with the real
feed instead of derived recent-booking guesses.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-18 16:06:13 +02:00
1 parent e5cb9bd543
commit d86cba61e3
18 files changed
+634 -28

No files matched your search

@@ -226,6 +226,17 @@ func (s *CustomerService) RescheduleBooking(ctx context.Context, reference strin
}
}
serviceName, _ := s.descriptors(ctx, booking)
_ = s.repo.AppendActivityEvent(ctx, db.ActivityEventParams{
TenantID: booking.TenantID,
ActorKind: "customer",
ActorName: booking.CustomerName,
Action: "booking.rescheduled",
EntityType: "booking",
EntityID: &booking.ID,
Summary: booking.CustomerName + " — " + serviceName,
})
// Send reschedule email (async)
go s.sendRescheduleEmail(booking, newStartsAt, newEndsAt)
@@ -250,6 +261,16 @@ func (s *CustomerService) CancelBooking(ctx context.Context, reference string, t
return err
}
_ = s.repo.CancelPendingReminderJobsForBooking(ctx, booking.ID)
serviceName, _ := s.descriptors(ctx, booking)
_ = s.repo.AppendActivityEvent(ctx, db.ActivityEventParams{
TenantID: booking.TenantID,
ActorKind: "customer",
ActorName: booking.CustomerName,
Action: "booking.cancelled",
EntityType: "booking",
EntityID: &booking.ID,
Summary: booking.CustomerName + " — " + serviceName,
})
// Send cancellation email (async)
go s.sendCancellationEmail(booking)
@@ -534,6 +555,15 @@ func (s *CustomerService) MoveBookingToSession(ctx context.Context, reference st
updated := booking
updated.ClassSessionID = &target.ID
_ = s.repo.AppendActivityEvent(ctx, db.ActivityEventParams{
TenantID: booking.TenantID,
ActorKind: "customer",
ActorName: booking.CustomerName,
Action: "booking.moved",
EntityType: "booking",
EntityID: &booking.ID,
Summary: booking.CustomerName + " — " + target.Title,
})
go s.sendRescheduleEmail(updated, target.StartsAt, target.EndsAt)
return domain.MoveBookingResponse{Status: newStatus}, nil