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

+42
View File
@@ -0,0 +1,42 @@
package db
import (
"context"
)
func (r *PGRepository) AppendActivityEvent(ctx context.Context, params ActivityEventParams) error {
_, err := r.pool.Exec(ctx, `
INSERT INTO activity_events
(tenant_id, actor_kind, actor_subject, actor_name, action, entity_type, entity_id, summary)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`, params.TenantID, params.ActorKind, params.ActorSubject, params.ActorName,
params.Action, params.EntityType, params.EntityID, params.Summary)
return err
}
func (r *PGRepository) ListActivityEvents(ctx context.Context, tenantID string, limit int) ([]ActivityEventRecord, error) {
rows, err := r.pool.Query(ctx, `
SELECT id, tenant_id, actor_kind, actor_name, action, entity_type, entity_id, summary, created_at
FROM activity_events
WHERE tenant_id = $1
ORDER BY created_at DESC
LIMIT $2
`, tenantID, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var events []ActivityEventRecord
for rows.Next() {
var rec ActivityEventRecord
if err := rows.Scan(
&rec.ID, &rec.TenantID, &rec.ActorKind, &rec.ActorName,
&rec.Action, &rec.EntityType, &rec.EntityID, &rec.Summary, &rec.CreatedAt,
); err != nil {
return nil, err
}
events = append(events, rec)
}
return events, rows.Err()
}