Skip to content

Auto-assign on reply (per-inbox toggle)#299

Open
mageaustralia wants to merge 1 commit intoabhinavxd:mainfrom
mageaustralia:feat/auto-assign-on-reply
Open

Auto-assign on reply (per-inbox toggle)#299
mageaustralia wants to merge 1 commit intoabhinavxd:mainfrom
mageaustralia:feat/auto-assign-on-reply

Conversation

@mageaustralia
Copy link
Copy Markdown

What this changes

When an agent replies to an unassigned email conversation, the conversation is automatically assigned to that agent. Off by default; enabled per-inbox under Admin > Inboxes > Edit.

Common workflow: a triage agent picks up an inbox, the first agent who actually starts working a ticket claims it. Saves a click after every reply.

Notes worth flagging during review

These caught us out in our fork — both shipped clean here, but worth mentioning so you know I looked at them:

  • Inbox Update function unmarshals config into two LOCAL structs (currentCfg + updateCfg) instead of imodels.Config, then re-marshals back. Anything not on those local structs is silently dropped on every save. Added auto_assign_on_reply to both, and left a code comment on the block calling out the trap so the next field-add doesn't fall in it.
  • vee-validate/zod strips unknown fields, so the toggle has to be in formSchema.js as well as the SwitchField — without it the toggle ticks in the UI but never reaches the API.
  • NewInbox.vue and EditInbox.vue build their config payloads independently. Both updated. EditInbox also needed the form-load mapping (config.auto_assign_on_reply → top-level).

Audit trail

Auto-assignment records a self_assign activity through the same code path as manual self-assignment (RecordAssigneeUserChange), so the conversation timeline shows "Agent X self-assigned this conversation" — identical to a manual self-assign. Felt important to make this visible rather than silent.

A known small race

Between the GetConversation check and the UpdateAssignee write, concurrent agent replies could both observe the conversation as unassigned and both attempt to claim it. Last write wins, no corruption. An atomic UPDATE ... WHERE assigned_user_id IS NULL would be tighter; left out of this PR to keep the surface small. Happy to add if you'd prefer that direction.

Test plan

  • Toggle off (default): reply to an unassigned conversation → no assignee change
  • Toggle on: reply to an unassigned conversation → conversation assigned to replier, "self-assigned" activity entry appears
  • Toggle on: reply to a conversation already assigned to another agent → no change
  • Toggle on: agent replies twice in a row → only the first triggers the auto-assign (the second sees themselves as assignee)
  • Edit inbox, save without changing the toggle → setting persists (smoke-test for the local-struct trap above)
  • Create a new inbox with the toggle on → setting saved (smoke-test for the zod-strip trap)

When an agent replies to an unassigned email conversation, the
conversation is automatically assigned to that agent. Off by default;
enabled per-inbox under Admin > Inboxes > Edit.

Plumbing notes worth mentioning since they're easy to miss:

- The inbox Update function unmarshals config into two LOCAL structs
  (currentCfg + updateCfg) rather than imodels.Config, then re-marshals
  back. Anything not on those local structs is silently dropped on every
  save. Added auto_assign_on_reply to both, and left a comment on the
  block calling out the trap so the next field-add doesn't fall in it.

- The vee-validate/zod schema strips fields it doesn't know about, so
  the toggle has to be in formSchema.js as well as the SwitchField, or
  the value never makes it to the API.

- NewInbox.vue and EditInbox.vue build their config payloads
  independently. Both updated. EditInbox also needed the form-load
  mapping (config.auto_assign_on_reply -> top-level field).

- Auto-assign records a self-assign activity via the same code path as
  manual self-assignment, so the audit trail looks identical.

There's a small race window between the GetConversation check and the
UpdateAssignee write — concurrent agent replies could both see the
conversation as unassigned and both attempt to claim it. Last write
wins, no corruption. Atomic "claim if NULL" SQL would be cleaner; left
out of this PR to keep the surface small. Happy to add if you'd prefer.
@abhinavxd
Copy link
Copy Markdown
Owner

Hey thanks for the PRs!

This feature was requested by many at my workplace.
I am vacationing, will review/merge all pending ones once I am back.

thanks again!

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.

2 participants