feat: add feedback resolver

This commit is contained in:
Krispire Admin
2026-06-03 23:43:17 +05:30
parent 61b91c473b
commit e9a96f1ff9
7 changed files with 211 additions and 16 deletions
+23 -5
View File
@@ -32,16 +32,17 @@ func (r *FeedbackRepo) Create(ctx context.Context, userID *string, category, mes
// FindByID returns one feedback row joined with the submitter's
// handle + name + email if a user_id is present. Used after Create to
// assemble the admin notification email with the proper author block.
// assemble the admin notification email with the proper author block,
// and after MarkResolved to assemble the author thank-you email.
func (r *FeedbackRepo) FindByID(ctx context.Context, id string) (*model.Feedback, error) {
f := &model.Feedback{}
err := r.pg.Pool.QueryRow(ctx, `
SELECT f.id, f.user_id, f.category, f.message, f.contact_email, f.created_at,
SELECT f.id, f.user_id, f.category, f.message, f.contact_email, f.created_at, f.resolved_at,
u.handle, u.name, u.email
FROM feedback f
LEFT JOIN users u ON u.id = f.user_id
WHERE f.id = $1`, id,
).Scan(&f.ID, &f.UserID, &f.Category, &f.Message, &f.ContactEmail, &f.CreatedAt,
).Scan(&f.ID, &f.UserID, &f.Category, &f.Message, &f.ContactEmail, &f.CreatedAt, &f.ResolvedAt,
&f.AuthorHandle, &f.AuthorName, &f.AuthorEmail)
if err != nil {
return nil, fmt.Errorf("find feedback: %w", err)
@@ -49,6 +50,23 @@ func (r *FeedbackRepo) FindByID(ctx context.Context, id string) (*model.Feedback
return f, nil
}
// MarkResolved stamps resolved_at = now() on a still-open row. The
// `resolved_at IS NULL` guard makes this idempotent and one-way: the
// first call returns true (newly resolved), any subsequent call — or a
// call on an unknown id — returns false so the caller knows NOT to fire
// the thank-you email a second time.
func (r *FeedbackRepo) MarkResolved(ctx context.Context, id string) (resolved bool, err error) {
tag, err := r.pg.Pool.Exec(ctx, `
UPDATE feedback
SET resolved_at = now()
WHERE id = $1 AND resolved_at IS NULL`, id,
)
if err != nil {
return false, fmt.Errorf("mark feedback resolved: %w", err)
}
return tag.RowsAffected() == 1, nil
}
// Recent returns the most-recent N feedback rows joined with users
// for display on /stats. NULL author columns mean anonymous (or the
// original submitter's account was since deleted).
@@ -57,7 +75,7 @@ func (r *FeedbackRepo) Recent(ctx context.Context, limit int) ([]model.Feedback,
limit = 25
}
rows, err := r.pg.Pool.Query(ctx, `
SELECT f.id, f.user_id, f.category, f.message, f.contact_email, f.created_at,
SELECT f.id, f.user_id, f.category, f.message, f.contact_email, f.created_at, f.resolved_at,
u.handle, u.name
FROM feedback f
LEFT JOIN users u ON u.id = f.user_id
@@ -71,7 +89,7 @@ func (r *FeedbackRepo) Recent(ctx context.Context, limit int) ([]model.Feedback,
out := []model.Feedback{}
for rows.Next() {
var f model.Feedback
if err := rows.Scan(&f.ID, &f.UserID, &f.Category, &f.Message, &f.ContactEmail, &f.CreatedAt,
if err := rows.Scan(&f.ID, &f.UserID, &f.Category, &f.Message, &f.ContactEmail, &f.CreatedAt, &f.ResolvedAt,
&f.AuthorHandle, &f.AuthorName); err != nil {
return nil, fmt.Errorf("scan feedback: %w", err)
}