In-site feedback reply (plain-text v1)
Reply to a reader from the admin inbox instead of a mailto. Per Codex: keep v1
plain text (no rich editor — defers the user's bold/bullets ask as a fast-follow).
* DB: feedback_replies table (feedback_id, user_id, message, sent_to, sent_at),
created on the live DB.
* email_send.send_feedback_reply: plain-text "Re: Your Upbeat Bytes feedback"
with a quoted context block, no analytics/account details.
* API: POST /api/admin/feedback/{id}/reply — admin-gated, requires the feedback
exists (404) and has a contact_email (400), trims+caps the message; sends via
SMTP and only records the reply on success (502 on send failure so the UI keeps
the draft); marks the item read. Feedback list now includes each item's replies.
* Frontend: inline composer (Send/Cancel, sending state, error keeps draft) +
reply thread under the message; Reply only shows when there's an address,
else "No reply address".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,21 @@ def send_feedback(to: str, category: str, message: str, contact: str | None, who
|
||||
send_email(to, subject, text)
|
||||
|
||||
|
||||
def send_feedback_reply(to: str, reply_message: str, original_message: str) -> None:
|
||||
"""Reply to a reader's feedback from the admin inbox (plain text). Quotes
|
||||
their original note for context; exposes no analytics/account details."""
|
||||
subject = "Re: Your Upbeat Bytes feedback"
|
||||
quoted = "\n".join("> " + line for line in (original_message or "").splitlines())
|
||||
text = (
|
||||
f"{reply_message}\n\n"
|
||||
"—\n"
|
||||
"In reply to your note to Upbeat Bytes:\n"
|
||||
f"{quoted}\n\n"
|
||||
"Thanks for reaching out.\n— Upbeat Bytes\n"
|
||||
)
|
||||
send_email(to, subject, text)
|
||||
|
||||
|
||||
def send_magic_link(to: str, link: str) -> None:
|
||||
"""Send a calm, single-purpose sign-in email."""
|
||||
subject = "Your Upbeat Bytes sign-in link"
|
||||
|
||||
Reference in New Issue
Block a user