mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 07:41:16 +00:00
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:
1 parent
e5cb9bd543
commit
d86cba61e3
18 files changed
+634
-28
No files matched your search
@@ -142,6 +142,10 @@ type Repository interface {
|
||||
ListStaffFeedTokens(ctx context.Context, tenantID string) ([]StaffFeedTokenRecord, error)
|
||||
RotateTenantFeedToken(ctx context.Context, tenantID string) (string, error)
|
||||
RotateStaffFeedToken(ctx context.Context, staffID string) (string, error)
|
||||
|
||||
// Activity feed
|
||||
AppendActivityEvent(ctx context.Context, params ActivityEventParams) error
|
||||
ListActivityEvents(ctx context.Context, tenantID string, limit int) ([]ActivityEventRecord, error)
|
||||
}
|
||||
|
||||
type TenantRecord struct {
|
||||
@@ -298,6 +302,30 @@ type StaffFeedTokenRecord struct {
|
||||
FeedToken string
|
||||
}
|
||||
|
||||
// ActivityEventRecord / ActivityEventParams back the per-tenant audit feed.
|
||||
type ActivityEventRecord struct {
|
||||
ID string
|
||||
TenantID string
|
||||
ActorKind string
|
||||
ActorName string
|
||||
Action string
|
||||
EntityType string
|
||||
EntityID *string
|
||||
Summary string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type ActivityEventParams struct {
|
||||
TenantID string
|
||||
ActorKind string
|
||||
ActorSubject string
|
||||
ActorName string
|
||||
Action string
|
||||
EntityType string
|
||||
EntityID *string
|
||||
Summary string
|
||||
}
|
||||
|
||||
type CreateStaffParams struct {
|
||||
TenantID string
|
||||
LocationID *string
|
||||
@@ -819,6 +847,7 @@ type MemoryRepository struct {
|
||||
emailTemplates []EmailTemplateRecord
|
||||
tenantFeedToken string
|
||||
staffFeedTokens map[string]string
|
||||
activityEvents []ActivityEventRecord
|
||||
}
|
||||
|
||||
func NewMemoryRepository() *MemoryRepository {
|
||||
@@ -945,6 +974,26 @@ func NewMemoryRepository() *MemoryRepository {
|
||||
staffFeedTokens: map[string]string{
|
||||
staffID: "demo-staff-feed-token",
|
||||
},
|
||||
activityEvents: []ActivityEventRecord{
|
||||
{
|
||||
ID: uuid.NewString(), TenantID: tenantID, ActorKind: "staff", ActorName: "Demo Stylist",
|
||||
Action: "booking.cancelled", EntityType: "booking",
|
||||
Summary: "Carol White — Consultation",
|
||||
CreatedAt: now.Add(-50 * time.Hour),
|
||||
},
|
||||
{
|
||||
ID: uuid.NewString(), TenantID: tenantID, ActorKind: "system",
|
||||
Action: "booking.promoted", EntityType: "booking",
|
||||
Summary: "Bob Smith — Small group mobility class",
|
||||
CreatedAt: now.Add(-26 * time.Hour),
|
||||
},
|
||||
{
|
||||
ID: uuid.NewString(), TenantID: tenantID, ActorKind: "customer", ActorName: "Alice Johnson",
|
||||
Action: "booking.created", EntityType: "booking",
|
||||
Summary: "Alice Johnson — Relaxation massage",
|
||||
CreatedAt: now.Add(-3 * time.Hour),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1315,6 +1364,35 @@ func (r *MemoryRepository) RotateStaffFeedToken(_ context.Context, staffID strin
|
||||
return "", pgx.ErrNoRows
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) AppendActivityEvent(_ context.Context, params ActivityEventParams) error {
|
||||
if params.TenantID != r.tenant.ID {
|
||||
return nil
|
||||
}
|
||||
r.activityEvents = append(r.activityEvents, ActivityEventRecord{
|
||||
ID: uuid.NewString(),
|
||||
TenantID: params.TenantID,
|
||||
ActorKind: params.ActorKind,
|
||||
ActorName: params.ActorName,
|
||||
Action: params.Action,
|
||||
EntityType: params.EntityType,
|
||||
EntityID: params.EntityID,
|
||||
Summary: params.Summary,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) ListActivityEvents(_ context.Context, tenantID string, limit int) ([]ActivityEventRecord, error) {
|
||||
if tenantID != r.tenant.ID {
|
||||
return nil, nil
|
||||
}
|
||||
var events []ActivityEventRecord
|
||||
for i := len(r.activityEvents) - 1; i >= 0 && len(events) < limit; i-- {
|
||||
events = append(events, r.activityEvents[i])
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRepository) ListAvailabilityRulesByTenant(_ context.Context, tenantID string) ([]AvailabilityRuleRecord, error) {
|
||||
if tenantID != r.tenant.ID {
|
||||
return nil, nil
|
||||
|
||||
Reference in new issue
Block a user