Files
b981ba766f feat: support bulk label removal (#14534)
Adds bulk label removal alongside the existing assign-label action for
conversations and contacts, so teams can clean up labels across selected
records without opening each item individually.

For conversations, the remove dropdown is scoped to labels that are
actually applied across the current selection — so agents no longer see
(or accidentally "remove") labels that aren't on any of the selected
items. For contacts, the dropdown still lists all account labels for
now; label data isn't carried on the contact list payload today, so
scoping the contact remove menu cleanly is being tracked as a follow-up.

## Closes

N/A

## How to test

- Open the conversation list, select multiple conversations, open
**Remove labels**, and confirm the dropdown only lists labels that are
applied to at least one selected conversation. Pick a label and confirm
it's removed from the selection.
- Open Contacts, select multiple contacts, use **Remove Labels**, choose
a label, and confirm the selected contacts are refreshed without that
label.
- Verify **Assign Labels** still works for conversations and contacts,
and continues to show every available label.

## What changed

- Adds an `action` prop to the shared `BulkLabelActions` dropdown so it
can render in `assign` or `remove` mode.
- Wires conversation bulk remove to the existing `labels.remove` backend
path and filters the dropdown to the union of labels applied across the
selected conversations.
- Adds contact bulk remove support through
`Contacts::BulkRemoveLabelsService`, routed by
`Contacts::BulkActionService`.
- Raises contact label save failures instead of reporting a successful
bulk action when a contact update is invalid.

## Follow-ups

- Scope the contact remove dropdown to applied labels (needs a
lightweight endpoint, or eventually `cached_label_list` on `Contact`).

## Verification

Conversation bulk remove selector:

<img width="1680" height="1050" alt="Conversation bulk remove label
selector"
src="https://github.com/user-attachments/assets/2dba4a06-c497-45e1-85b0-e700164b6b2f"
/>

Contact bulk remove selector:

<img width="1680" height="1050" alt="Contact bulk remove label selector"
src="https://github.com/user-attachments/assets/b3b89959-5978-4064-b5f9-82b1a3e571dc"
/>

Video proof:


https://github.com/user-attachments/assets/fffafe19-4e1c-4e2a-a135-c7182c06bb4d

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
2026-05-26 15:23:51 +05:30

47 lines
2.4 KiB
JSON

{
"BULK_ACTION": {
"CONVERSATIONS_SELECTED": "{conversationCount} selected",
"NONE": "None",
"CLEAR_SELECTION": "Clear",
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
"UNASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
"YES": "Yes",
"CANCEL": "Cancel",
"SEARCH_INPUT_PLACEHOLDER": "Search",
"ASSIGN_AGENT_TOOLTIP": "Assign agent",
"ASSIGN_TEAM_TOOLTIP": "Assign team",
"ASSIGN_SUCCESFUL": "Conversations assigned successfully.",
"ASSIGN_FAILED": "Failed to assign conversations. Please try again.",
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
"UPDATE": {
"CHANGE_STATUS": "Change status",
"SNOOZE_UNTIL": "Snooze",
"UPDATE_SUCCESFUL": "Conversation status updated successfully.",
"UPDATE_FAILED": "Failed to update conversations. Please try again."
},
"RESOLVE": {
"ALL_MISSING_ATTRIBUTES": "Cannot resolve conversations due to missing required attributes",
"PARTIAL_SUCCESS": "Some conversations need required attributes before resolving and were skipped"
},
"LABELS": {
"ASSIGN_LABELS": "Assign labels",
"REMOVE_LABELS": "Remove labels",
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
"REMOVE_SELECTED_LABELS": "Remove selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again.",
"REMOVE_SUCCESFUL": "Labels removed successfully.",
"REMOVE_FAILED": "Failed to remove labels. Please try again."
},
"TEAMS": {
"NONE": "None",
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
"ASSIGN_SUCCESFUL": "Teams assigned successfully.",
"ASSIGN_FAILED": "Failed to assign team. Please try again."
}
}
}