Skip to content

Commit 4e4f07f

Browse files
authored
Merge pull request #67 from jleroy/feature/improve-tags-actions
Add new actions "Add tags" and "Remove tags" for macros and automations
2 parents 19a507c + facce8b commit 4e4f07f

File tree

11 files changed

+125
-29
lines changed

11 files changed

+125
-29
lines changed

cmd/conversation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ func handleUpdateConversationtags(r *fastglue.Request) error {
472472
return sendErrorEnvelope(r, err)
473473
}
474474

475-
if err := app.conversation.UpsertConversationTags(uuid, tagNames, user); err != nil {
475+
if err := app.conversation.SetConversationTags(uuid, models.ActionSetTags, tagNames, user); err != nil {
476476
return sendErrorEnvelope(r, err)
477477
}
478478
return r.SendEnvelope(true)

cmd/macro.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ func isMacroActionAllowed(action string) bool {
292292
switch action {
293293
case autoModels.ActionSendPrivateNote, autoModels.ActionReply:
294294
return false
295-
case autoModels.ActionAssignTeam, autoModels.ActionAssignUser, autoModels.ActionSetStatus, autoModels.ActionSetPriority, autoModels.ActionSetTags:
295+
case autoModels.ActionAssignTeam, autoModels.ActionAssignUser, autoModels.ActionSetStatus, autoModels.ActionSetPriority, autoModels.ActionAddTags, autoModels.ActionSetTags, autoModels.ActionRemoveTags:
296296
return true
297297
default:
298298
return false

frontend/src/composables/useConversationFilters.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,17 @@ export function useConversationFilters () {
221221
type: FIELD_TYPE.SELECT,
222222
options: slaStore.options
223223
},
224+
add_tags: {
225+
label: 'Add tags',
226+
type: FIELD_TYPE.TAG
227+
},
224228
set_tags: {
225229
label: 'Set tags',
226230
type: FIELD_TYPE.TAG
231+
},
232+
remove_tags: {
233+
label: 'Remove tags',
234+
type: FIELD_TYPE.TAG
227235
}
228236
}))
229237

@@ -248,9 +256,17 @@ export function useConversationFilters () {
248256
type: FIELD_TYPE.SELECT,
249257
options: cStore.priorityOptions
250258
},
259+
add_tags: {
260+
label: 'Add tags',
261+
type: FIELD_TYPE.TAG
262+
},
251263
set_tags: {
252264
label: 'Set tags',
253265
type: FIELD_TYPE.TAG
266+
},
267+
remove_tags: {
268+
label: 'Remove tags',
269+
type: FIELD_TYPE.TAG
254270
}
255271
}))
256272

frontend/src/features/command/CommandBox.vue

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,21 @@
123123
:size="10"
124124
class="shrink-0 text-primary"
125125
/>
126+
<Tags
127+
v-else-if="action.type === 'add_tags'"
128+
:size="10"
129+
class="shrink-0 text-primary"
130+
/>
126131
<Tags
127132
v-else-if="action.type === 'set_tags'"
128133
:size="10"
129134
class="shrink-0 text-primary"
130135
/>
136+
<Tags
137+
v-else-if="action.type === 'remove_tags'"
138+
:size="10"
139+
class="shrink-0 text-primary"
140+
/>
131141
</div>
132142
<span class="truncate">{{ getActionLabel(action) }}</span>
133143
</div>
@@ -260,7 +270,9 @@ const getActionLabel = computed(() => (action) => {
260270
assign_team: t('globals.messages.assignTeam'),
261271
set_status: t('globals.messages.setStatus'),
262272
set_priority: t('globals.messages.setPriority'),
263-
set_tags: t('globals.messages.setTags')
273+
add_tags: t('globals.messages.addTags'),
274+
set_tags: t('globals.messages.setTags'),
275+
remove_tags: t('globals.messages.removeTags')
264276
}
265277
return `${prefixes[action.type]}: ${action.display_value.length > 0 ? action.display_value.join(', ') : action.value.join(', ')}`
266278
})

frontend/src/features/conversation/MacroActionsPreview.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ const getIcon = (type) =>
6060
assign_user: User,
6161
set_status: MessageSquare,
6262
set_priority: Flag,
63-
set_tags: Tags
63+
add_tags: Tags,
64+
set_tags: Tags,
65+
remove_tags: Tags
6466
})[type]
6567
6668
const getDisplayValue = (action) => {
@@ -80,8 +82,12 @@ const getTooltip = (action) => {
8082
return `${t('globals.messages.setStatus')}: ${getDisplayValue(action)}`
8183
case 'set_priority':
8284
return `${t('globals.messages.setPriority')}: ${getDisplayValue(action)}`
85+
case 'add_tags':
86+
return `${t('globals.messages.addTags')}: ${getDisplayValue(action)}`
8387
case 'set_tags':
8488
return `${t('globals.messages.setTags')}: ${getDisplayValue(action)}`
89+
case 'remove_tags':
90+
return `${t('globals.messages.removeTags')}: ${getDisplayValue(action)}`
8591
default:
8692
return `${t('globals.terms.action')}: ${action.type}, ${t('globals.terms.value')}: ${getDisplayValue(action)}`
8793
}

i18n/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@
147147
"globals.messages.assignTeam": "Assign to team",
148148
"globals.messages.setStatus": "Set status",
149149
"globals.messages.setPriority": "Set priority",
150+
"globals.messages.addTags": "Add tags",
150151
"globals.messages.setTags": "Set tags",
152+
"globals.messages.removeTags": "Remove tags",
151153
"globals.messages.snooze": "Snooze",
152154
"globals.messages.resolve": "Resolve",
153155
"globals.messages.applyMacro": "Apply macro",

internal/automation/models/models.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ const (
1616
ActionSendPrivateNote = "send_private_note"
1717
ActionReply = "send_reply"
1818
ActionSetSLA = "set_sla"
19+
ActionAddTags = "add_tags"
1920
ActionSetTags = "set_tags"
21+
ActionRemoveTags = "remove_tags"
2022
ActionSendCSAT = "send_csat"
2123

2224
OperatorAnd = "AND"
@@ -68,7 +70,9 @@ var ActionPermissions = map[string]string{
6870
ActionSetPriority: authzModels.PermConversationsUpdatePriority,
6971
ActionSendPrivateNote: authzModels.PermMessagesWrite,
7072
ActionReply: authzModels.PermMessagesWrite,
73+
ActionAddTags: authzModels.PermConversationsUpdateTags,
7174
ActionSetTags: authzModels.PermConversationsUpdateTags,
75+
ActionRemoveTags: authzModels.PermConversationsUpdateTags,
7276
}
7377

7478
// RuleRecord represents a rule record in the database

internal/conversation/conversation.go

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ type queries struct {
203203
UpdateConversationLastMessage *sqlx.Stmt `query:"update-conversation-last-message"`
204204
InsertConversationParticipant *sqlx.Stmt `query:"insert-conversation-participant"`
205205
InsertConversation *sqlx.Stmt `query:"insert-conversation"`
206-
UpsertConversationTags *sqlx.Stmt `query:"upsert-conversation-tags"`
206+
AddConversationTags *sqlx.Stmt `query:"add-conversation-tags"`
207+
SetConversationTags *sqlx.Stmt `query:"set-conversation-tags"`
208+
RemoveConversationTags *sqlx.Stmt `query:"remove-conversation-tags"`
207209
GetConversationTags *sqlx.Stmt `query:"get-conversation-tags"`
208210
UnassignOpenConversations *sqlx.Stmt `query:"unassign-open-conversations"`
209211
ReOpenConversation *sqlx.Stmt `query:"re-open-conversation"`
@@ -657,32 +659,62 @@ func (c *Manager) GetDashboardChart(userID, teamID int) (json.RawMessage, error)
657659
return stats, nil
658660
}
659661

660-
// UpsertConversationTags upserts the tags associated with a conversation.
661-
func (c *Manager) UpsertConversationTags(uuid string, tagNames []string, actor umodels.User) error {
662-
// Get the existing tags.
663-
tags, err := c.getConversationTags(uuid)
662+
// SetConversationTags sets the tags associated with a conversation.
663+
func (c *Manager) SetConversationTags(uuid string, action string, tagNames []string, actor umodels.User) error {
664+
// Get current tags list.
665+
prevTags, err := c.getConversationTags(uuid)
664666
if err != nil {
665-
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.tag}"), nil)
667+
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.tag}"), nil)
666668
}
667669

668-
// Upsert the tags.
669-
if _, err := c.q.UpsertConversationTags.Exec(uuid, pq.Array(tagNames)); err != nil {
670-
c.lo.Error("error upserting conversation tags", "error", err)
671-
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.tag}"), nil)
670+
// Add specified tags, ignore existing ones.
671+
if action == amodels.ActionAddTags {
672+
if _, err := c.q.AddConversationTags.Exec(uuid, pq.Array(tagNames)); err != nil {
673+
c.lo.Error("error adding conversation tags", "error", err)
674+
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.tag}"), nil)
675+
}
672676
}
673677

674-
// Find the newly added tags.
675-
newTags := []string{}
676-
for _, tag := range tagNames {
677-
if slices.Contains(tags, tag) {
678+
// Set specified tags and remove all other existing ones.
679+
if action == amodels.ActionSetTags {
680+
if _, err := c.q.SetConversationTags.Exec(uuid, pq.Array(tagNames)); err != nil {
681+
c.lo.Error("error setting conversation tags", "error", err)
682+
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.tag}"), nil)
683+
}
684+
}
685+
686+
// Delete specified tags, ignore all others.
687+
if action == amodels.ActionRemoveTags {
688+
if _, err := c.q.RemoveConversationTags.Exec(uuid, pq.Array(tagNames)); err != nil {
689+
c.lo.Error("error removing conversation tags", "error", err)
690+
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.tag}"), nil)
691+
}
692+
}
693+
694+
// Get updated tags list.
695+
newTags, err := c.getConversationTags(uuid)
696+
if err != nil {
697+
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.tag}"), nil)
698+
}
699+
700+
// Find actually removed tags.
701+
for _, tag := range prevTags {
702+
if slices.Contains(newTags, tag) {
678703
continue
679704
}
680-
newTags = append(newTags, tag)
705+
// Record the removed tags as activities.
706+
if err := c.RecordTagRemoval(uuid, tag, actor); err != nil {
707+
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.tag}"), nil)
708+
}
681709
}
682710

683-
// Record the new tags as activities.
711+
// Find actually added tags.
684712
for _, tag := range newTags {
685-
if err := c.RecordTagChange(uuid, tag, actor); err != nil {
713+
if slices.Contains(prevTags, tag) {
714+
continue
715+
}
716+
// Record the added tags as activities.
717+
if err := c.RecordTagAddition(uuid, tag, actor); err != nil {
686718
return envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.tag}"), nil)
687719
}
688720
}
@@ -842,8 +874,8 @@ func (m *Manager) ApplyAction(action amodels.RuleAction, conv models.Conversatio
842874
case amodels.ActionSetSLA:
843875
slaID, _ := strconv.Atoi(action.Value[0])
844876
return m.ApplySLA(conv, slaID, user)
845-
case amodels.ActionSetTags:
846-
return m.UpsertConversationTags(conv.UUID, action.Value, user)
877+
case amodels.ActionAddTags, amodels.ActionSetTags, amodels.ActionRemoveTags:
878+
return m.SetConversationTags(conv.UUID, action.Type, action.Value, user)
847879
case amodels.ActionSendCSAT:
848880
return m.SendCSATReply(user.ID, conv)
849881
default:

internal/conversation/message.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,14 @@ func (m *Manager) RecordSLASet(conversationUUID string, slaName string, actor um
445445
return m.InsertConversationActivity(models.ActivitySLASet, conversationUUID, slaName, actor)
446446
}
447447

448-
// RecordTagChange records an activity for a tag change.
449-
func (m *Manager) RecordTagChange(conversationUUID string, tag string, actor umodels.User) error {
450-
return m.InsertConversationActivity(models.ActivityTagChange, conversationUUID, tag, actor)
448+
// RecordTagAddition records an activity for a tag addition.
449+
func (m *Manager) RecordTagAddition(conversationUUID string, tag string, actor umodels.User) error {
450+
return m.InsertConversationActivity(models.ActivityTagAdded, conversationUUID, tag, actor)
451+
}
452+
453+
// RecordTagRemoval records an activity for a tag removal.
454+
func (m *Manager) RecordTagRemoval(conversationUUID string, tag string, actor umodels.User) error {
455+
return m.InsertConversationActivity(models.ActivityTagRemoved, conversationUUID, tag, actor)
451456
}
452457

453458
// InsertConversationActivity inserts an activity message.
@@ -500,8 +505,10 @@ func (m *Manager) getMessageActivityContent(activityType, newValue, actorName st
500505
content = fmt.Sprintf("%s set priority to %s", actorName, newValue)
501506
case models.ActivityStatusChange:
502507
content = fmt.Sprintf("%s marked the conversation as %s", actorName, newValue)
503-
case models.ActivityTagChange:
508+
case models.ActivityTagAdded:
504509
content = fmt.Sprintf("%s added tag %s", actorName, newValue)
510+
case models.ActivityTagRemoved:
511+
content = fmt.Sprintf("%s removed tag %s", actorName, newValue)
505512
case models.ActivitySLASet:
506513
content = fmt.Sprintf("%s set %s SLA policy", actorName, newValue)
507514
default:

internal/conversation/models/models.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ var (
4444
ActivityAssignedUserChange = "assigned_user_change"
4545
ActivityAssignedTeamChange = "assigned_team_change"
4646
ActivitySelfAssign = "self_assign"
47-
ActivityTagChange = "tag_change"
47+
ActivityTagAdded = "tag_added"
48+
ActivityTagRemoved = "tag_removed"
4849
ActivitySLASet = "sla_set"
4950

5051
ContentTypeText = "text"

0 commit comments

Comments
 (0)