Skip to content

Make logged communications editable inline, like comments#1942

Open
maebeale wants to merge 1 commit into
mainfrom
maebeale/editable-notifications
Open

Make logged communications editable inline, like comments#1942
maebeale wants to merge 1 commit into
mainfrom
maebeale/editable-notifications

Conversation

@maebeale

@maebeale maebeale commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

🤖 suggested review level: 3 Read 📖 contained inline-edit logic reusing the comment edit-toggle pattern across 3 edit pages

What is the goal of this PR and why is this important?

  • Manually logged communications on the person, registration, and scholarship edit pages were add-only — once saved they turned read-only, so a typo or wrong channel couldn't be fixed without the console.
  • They now get the same inline view ⇄ edit toggle the comments box already has: admins can edit or remove a logged entry in place and save it with the parent form.
  • Automated notifications (confirmations, etc.) stay read-only — only manual_log entries owned by the record are editable.

How did you approach the change?

  • Generalized the comment comment-edit-toggle Stimulus controller into one reusable edit-toggle (view/edit CSS classes passed as values; comment body-sync kept as an optional, class-driven feature). Comments and communications now share it — no near-duplicate controller.
  • _notification_fields now renders a read-only .notification-view row + hidden .notification-edit form for persisted entries (mirrors _comment_fields); extracted _notification_row (shared read-only row) and _notification_form (shared editable fields).
  • Permitted :id/:_destroy on notifications_attributes and added allow_destroy: true so existing logs update/remove through the parent form.
  • Fixed the scholarship partial passing event_registration: instead of record: (hidden noticeable fields weren't rendering).

Anything else to add?

  • The persisted list is matched by recipient email (broad), but only association-owned manual_log entries are editable — the rest render read-only, so no page loses visibility of automated communications.

Manual-log communications on the person, registration, and scholarship
edit pages were add-only: once saved they became read-only, so a typo or
wrong channel couldn't be corrected without touching the console. This
gives them the same inline view/edit toggle the comments box already has,
so admins can fix or remove a logged entry in place and save it with the
parent form. Automated notifications stay read-only.

Rather than add a near-duplicate Stimulus controller, the comment
edit-toggle controller is generalized into a reusable `edit-toggle` that
takes the view/edit CSS classes as values (with the comment body-sync as
an optional, class-driven feature), and both boxes now share it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 5, 2026 04:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

toggle() {
this.editing = !this.editing;

// When leaving edit mode, sync edit textareas into their truncated view.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Generalized from comment-edit-toggle. The body-sync (updating the truncated view from the textarea on exit) is now opt-in via bodyClassValue — comments set it, communications leave it blank since their view row has no single truncated body span.

<% notifications = email.present? ? Notification.email(email).order(created_at: :desc) : Notification.none %>
<section class="overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm">
<% new_notifications = person.notifications.select(&:new_record?) %>
<% editable_notifications = notifications.select { |n| n.manual_log? && n.noticeable_type == Person.name && n.noticeable_id == person.id } %>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: The list is matched by recipient email (broad), but only manual_log entries whose noticeable is this record can be edited via nested attributes — automated notifications and email-matched ones owned by other records stay read-only.

Comment thread app/models/person.rb
reject_if: proc { |attrs| attrs["organization_id"].blank? }
accepts_nested_attributes_for :comments, allow_destroy: true, reject_if: proc { |attrs| attrs["body"].blank? }
accepts_nested_attributes_for :notifications, reject_if: proc { |attrs| attrs["email_subject"].blank? }
accepts_nested_attributes_for :notifications, allow_destroy: true, reject_if: proc { |attrs| attrs["email_subject"].blank? }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: allow_destroy: true (with :_destroy permitted in the controller) lets the inline "Remove" link delete a logged entry — the reject_if blank-subject guard is bypassed for destroys, matching how comments work.

@maebeale maebeale marked this pull request as ready for review July 5, 2026 11:27

@jmilljr24 jmilljr24 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts on the stimulus controller. Not required, just might be a good time to refactor while its being worked on.

#1392


// When leaving edit mode, sync edit textareas into their truncated view.
if (!this.editing && this.bodyClassValue) {
this.element.querySelectorAll(".nested-fields").forEach((item) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a good opportunity to update this controller to use stimulus targets vs. depending on class targets.

type="button"
class="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 cursor-pointer"
data-action="click->comment-edit-toggle#toggle"
data-action="click->edit-toggle#toggle"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data-action="click->edit-toggle#toggle"
data-action="edit-toggle#toggle"

Click is implicit on buttons

Comment on lines +22 to +24
viewClass: { type: String, default: "editable-view" },
editClass: { type: String, default: "editable-edit" },
bodyClass: { type: String, default: "" },

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
viewClass: { type: String, default: "editable-view" },
editClass: { type: String, default: "editable-edit" },
bodyClass: { type: String, default: "" },

I believe these could all be done with stimulus targets instead of setting a class as a selector.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants