@escalated-dev/escalated 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +380 -357
- package/package.json +85 -81
- package/src/components/ActiveChatsPanel.vue +246 -246
- package/src/components/AdminDashboard.stories.js +1423 -1423
- package/src/components/AttachmentList.vue +65 -65
- package/src/components/ChatActionBar.vue +226 -226
- package/src/components/ChatBubble.vue +162 -162
- package/src/components/ChatComposer.vue +308 -308
- package/src/components/ChatQueue.vue +222 -222
- package/src/components/ChatThread.vue +197 -197
- package/src/components/CustomTicketActions.stories.js +71 -0
- package/src/components/CustomTicketActions.vue +101 -0
- package/src/components/EscalatedLayout.vue +619 -598
- package/src/components/FollowButton.vue +20 -1
- package/src/components/MentionDropdown.vue +154 -154
- package/src/components/MetricComparison.vue +89 -89
- package/src/components/PercentileChart.vue +67 -67
- package/src/components/PeriodSelector.vue +98 -98
- package/src/components/PresenceIndicator.vue +228 -213
- package/src/components/ReplyComposer.vue +336 -336
- package/src/components/ReplyThread.vue +153 -153
- package/src/components/ReportChart.vue +395 -395
- package/src/components/ReportExportButton.vue +108 -108
- package/src/components/SaveViewDialog.vue +172 -172
- package/src/components/SavedViewSidebar.vue +208 -208
- package/src/components/SkillTagManager.vue +29 -22
- package/src/components/SnoozeButton.vue +247 -247
- package/src/components/TicketFilters.vue +270 -270
- package/src/components/TicketList.vue +655 -655
- package/src/components/TicketSidebar.vue +4 -0
- package/src/components/TicketSplitDialog.vue +98 -98
- package/src/components/TicketSubjects.stories.js +63 -0
- package/src/components/TicketSubjects.vue +107 -0
- package/src/components/TwoFactorSetup.vue +1 -37
- package/src/components/VariableTokenInput.vue +111 -111
- package/src/components/WorkflowActionConfig.vue +287 -287
- package/src/components/WorkflowActionList.vue +350 -350
- package/src/components/WorkflowConditionBuilder.vue +265 -265
- package/src/components/WorkflowTriggerSelector.vue +125 -125
- package/src/components/admin/newsletters/AnalyticsTiles.stories.js +14 -0
- package/src/components/admin/newsletters/AnalyticsTiles.vue +55 -0
- package/src/components/admin/newsletters/DeliveriesTable.stories.js +28 -0
- package/src/components/admin/newsletters/DeliveriesTable.vue +104 -0
- package/src/components/admin/newsletters/DynamicFilterBuilder.stories.js +11 -0
- package/src/components/admin/newsletters/DynamicFilterBuilder.vue +53 -0
- package/src/components/admin/newsletters/ListMemberTable.stories.js +12 -0
- package/src/components/admin/newsletters/ListMemberTable.vue +53 -0
- package/src/components/admin/newsletters/MarkdownEditor.stories.js +6 -0
- package/src/components/admin/newsletters/MarkdownEditor.vue +53 -0
- package/src/components/admin/newsletters/MergeFieldDropdown.stories.js +6 -0
- package/src/components/admin/newsletters/MergeFieldDropdown.vue +84 -0
- package/src/components/admin/newsletters/PreviewIframe.stories.js +7 -0
- package/src/components/admin/newsletters/PreviewIframe.vue +40 -0
- package/src/components/admin/newsletters/SummaryTiles.stories.js +8 -0
- package/src/components/admin/newsletters/SummaryTiles.vue +63 -0
- package/src/composables/useChat.js +187 -187
- package/src/composables/useI18n.js +30 -8
- package/src/composables/useMentions.js +134 -134
- package/src/composables/usePermissions.js +31 -0
- package/src/composables/useRealtime.js +182 -182
- package/src/index.js +85 -84
- package/src/locales/README.md +13 -0
- package/src/locales/ar.json +727 -727
- package/src/locales/de.json +690 -690
- package/src/locales/en.json +889 -729
- package/src/locales/es.json +691 -691
- package/src/locales/fr.json +691 -691
- package/src/locales/index.js +16 -16
- package/src/locales/it.json +727 -727
- package/src/locales/ja.json +727 -727
- package/src/locales/ko.json +727 -727
- package/src/locales/nl.json +727 -727
- package/src/locales/pl.json +727 -727
- package/src/locales/pt-BR.json +727 -727
- package/src/locales/ru.json +727 -727
- package/src/locales/tr.json +727 -727
- package/src/locales/zh-CN.json +727 -727
- package/src/pages/Admin/Newsletters/Compose.vue +192 -0
- package/src/pages/Admin/Newsletters/Edit.vue +24 -0
- package/src/pages/Admin/Newsletters/Index.vue +141 -0
- package/src/pages/Admin/Newsletters/Lists/Create.vue +64 -0
- package/src/pages/Admin/Newsletters/Lists/Index.vue +59 -0
- package/src/pages/Admin/Newsletters/Lists/Show.vue +53 -0
- package/src/pages/Admin/Newsletters/Settings.vue +85 -0
- package/src/pages/Admin/Newsletters/Show.vue +140 -0
- package/src/pages/Admin/Newsletters/Templates/Create.vue +11 -0
- package/src/pages/Admin/Newsletters/Templates/Index.vue +54 -0
- package/src/pages/Admin/Newsletters/Templates/Show.vue +65 -0
- package/src/pages/Admin/Newsletters/usePreview.js +44 -0
- package/src/pages/Admin/Reports/AgentDetail.vue +132 -132
- package/src/pages/Admin/Reports/AgentRanking.vue +246 -246
- package/src/pages/Admin/Reports/Cohorts.vue +180 -180
- package/src/pages/Admin/Reports/Comparison.vue +141 -141
- package/src/pages/Admin/Reports/Dashboard.vue +226 -226
- package/src/pages/Admin/Reports/ResolutionTimes.vue +164 -164
- package/src/pages/Admin/Reports/ResponseTimes.vue +164 -164
- package/src/pages/Admin/Reports/SlaTrends.vue +183 -183
- package/src/pages/Admin/Settings/SsoSettings.vue +157 -4
- package/src/pages/Admin/Settings/TwoFactor.vue +32 -6
- package/src/pages/Admin/Settings.vue +1008 -1008
- package/src/pages/Admin/Skills/Form.vue +164 -4
- package/src/pages/Admin/Skills/Index.vue +14 -3
- package/src/pages/Admin/Tickets/Index.vue +126 -126
- package/src/pages/Admin/Tickets/Show.vue +254 -249
- package/src/pages/Admin/Users/Index.vue +145 -0
- package/src/pages/Admin/Workflows/Builder.vue +207 -207
- package/src/pages/Admin/Workflows/Index.vue +235 -235
- package/src/pages/Admin/Workflows/Logs.vue +286 -286
- package/src/pages/Agent/TicketIndex.vue +112 -112
- package/src/pages/Agent/TicketShow.vue +547 -494
- package/src/pages/Customer/Index.vue +26 -1
- package/src/pages/Customer/KnowledgeBase/Article.vue +129 -129
- package/src/plugin.js +175 -175
- package/src/utils/mentionHighlight.js +24 -24
- package/src/widget/EscalatedWidget.vue +804 -804
- package/src/widget/index.js +254 -254
|
@@ -1,350 +1,350 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { ref, computed } from 'vue';
|
|
3
|
-
import WorkflowActionConfig from './WorkflowActionConfig.vue';
|
|
4
|
-
|
|
5
|
-
const props = defineProps({
|
|
6
|
-
modelValue: { type: Array, default: () => [] },
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
const emit = defineEmits(['update:modelValue']);
|
|
10
|
-
|
|
11
|
-
const actionTypes = [
|
|
12
|
-
{
|
|
13
|
-
value: 'assign_agent',
|
|
14
|
-
label: 'Assign Agent',
|
|
15
|
-
icon: 'M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z',
|
|
16
|
-
color: 'text-blue-400 bg-blue-500/10',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
value: 'change_status',
|
|
20
|
-
label: 'Change Status',
|
|
21
|
-
icon: 'M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
22
|
-
color: 'text-emerald-400 bg-emerald-500/10',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
value: 'change_priority',
|
|
26
|
-
label: 'Change Priority',
|
|
27
|
-
icon: 'M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12',
|
|
28
|
-
color: 'text-red-400 bg-red-500/10',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
value: 'add_tag',
|
|
32
|
-
label: 'Add Tag',
|
|
33
|
-
icon: 'M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z',
|
|
34
|
-
color: 'text-violet-400 bg-violet-500/10',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
value: 'remove_tag',
|
|
38
|
-
label: 'Remove Tag',
|
|
39
|
-
icon: 'M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z',
|
|
40
|
-
color: 'text-pink-400 bg-pink-500/10',
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
value: 'move_department',
|
|
44
|
-
label: 'Move Department',
|
|
45
|
-
icon: 'M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75z',
|
|
46
|
-
color: 'text-cyan-400 bg-cyan-500/10',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
value: 'add_note',
|
|
50
|
-
label: 'Add Internal Note',
|
|
51
|
-
icon: 'M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931z',
|
|
52
|
-
color: 'text-amber-400 bg-amber-500/10',
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
value: 'send_email',
|
|
56
|
-
label: 'Send Email Notification',
|
|
57
|
-
icon: 'M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75',
|
|
58
|
-
color: 'text-sky-400 bg-sky-500/10',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
value: 'send_webhook',
|
|
62
|
-
label: 'Send Webhook',
|
|
63
|
-
icon: 'M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244',
|
|
64
|
-
color: 'text-indigo-400 bg-indigo-500/10',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
value: 'delay',
|
|
68
|
-
label: 'Delay',
|
|
69
|
-
icon: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
70
|
-
color: 'text-orange-400 bg-orange-500/10',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
value: 'apply_macro',
|
|
74
|
-
label: 'Apply Macro',
|
|
75
|
-
icon: 'M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z',
|
|
76
|
-
color: 'text-yellow-400 bg-yellow-500/10',
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
value: 'close_ticket',
|
|
80
|
-
label: 'Close Ticket',
|
|
81
|
-
icon: 'M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
82
|
-
color: 'text-gray-400 bg-gray-500/10',
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
value: 'snooze',
|
|
86
|
-
label: 'Snooze',
|
|
87
|
-
icon: 'M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0',
|
|
88
|
-
color: 'text-teal-400 bg-teal-500/10',
|
|
89
|
-
},
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
const showTypeSelector = ref(false);
|
|
93
|
-
const expandedIndex = ref(null);
|
|
94
|
-
const dragIndex = ref(null);
|
|
95
|
-
|
|
96
|
-
const actions = computed(() => props.modelValue);
|
|
97
|
-
|
|
98
|
-
function getActionType(value) {
|
|
99
|
-
return actionTypes.find((t) => t.value === value) || actionTypes[0];
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function addAction(typeValue) {
|
|
103
|
-
const newActions = [...actions.value, { type: typeValue, config: {} }];
|
|
104
|
-
emit('update:modelValue', newActions);
|
|
105
|
-
showTypeSelector.value = false;
|
|
106
|
-
expandedIndex.value = newActions.length - 1;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function removeAction(index) {
|
|
110
|
-
const newActions = actions.value.filter((_, i) => i !== index);
|
|
111
|
-
emit('update:modelValue', newActions);
|
|
112
|
-
if (expandedIndex.value === index) expandedIndex.value = null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function updateConfig(index, config) {
|
|
116
|
-
const newActions = actions.value.map((a, i) => (i === index ? { ...a, config } : a));
|
|
117
|
-
emit('update:modelValue', newActions);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function toggleExpand(index) {
|
|
121
|
-
expandedIndex.value = expandedIndex.value === index ? null : index;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function getActionSummary(action) {
|
|
125
|
-
const c = action.config || {};
|
|
126
|
-
switch (action.type) {
|
|
127
|
-
case 'assign_agent':
|
|
128
|
-
return c.mode === 'round_robin'
|
|
129
|
-
? 'Round robin'
|
|
130
|
-
: c.mode === 'least_busy'
|
|
131
|
-
? 'Least busy'
|
|
132
|
-
: c.agent || 'Not configured';
|
|
133
|
-
case 'change_status':
|
|
134
|
-
return c.status || 'Not configured';
|
|
135
|
-
case 'change_priority':
|
|
136
|
-
return c.priority || 'Not configured';
|
|
137
|
-
case 'add_tag':
|
|
138
|
-
case 'remove_tag':
|
|
139
|
-
return c.tag || 'Not configured';
|
|
140
|
-
case 'move_department':
|
|
141
|
-
return c.department || 'Not configured';
|
|
142
|
-
case 'add_note':
|
|
143
|
-
return c.body ? c.body.substring(0, 40) + (c.body.length > 40 ? '...' : '') : 'Not configured';
|
|
144
|
-
case 'send_email':
|
|
145
|
-
return c.to || 'Not configured';
|
|
146
|
-
case 'send_webhook':
|
|
147
|
-
return c.url || 'Not configured';
|
|
148
|
-
case 'delay':
|
|
149
|
-
return `Wait ${c.duration || 1} ${c.unit || 'hours'}`;
|
|
150
|
-
case 'apply_macro':
|
|
151
|
-
return c.macro || 'Not configured';
|
|
152
|
-
case 'close_ticket':
|
|
153
|
-
return 'Close immediately';
|
|
154
|
-
case 'snooze':
|
|
155
|
-
return `${c.duration || 1} ${c.unit || 'hours'}`;
|
|
156
|
-
default:
|
|
157
|
-
return 'Not configured';
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Drag-and-drop reordering
|
|
162
|
-
function onDragStart(index) {
|
|
163
|
-
dragIndex.value = index;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function onDragOver(event, index) {
|
|
167
|
-
event.preventDefault();
|
|
168
|
-
if (dragIndex.value === null || dragIndex.value === index) return;
|
|
169
|
-
const newActions = [...actions.value];
|
|
170
|
-
const [moved] = newActions.splice(dragIndex.value, 1);
|
|
171
|
-
newActions.splice(index, 0, moved);
|
|
172
|
-
emit('update:modelValue', newActions);
|
|
173
|
-
dragIndex.value = index;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function onDragEnd() {
|
|
177
|
-
dragIndex.value = null;
|
|
178
|
-
}
|
|
179
|
-
</script>
|
|
180
|
-
|
|
181
|
-
<template>
|
|
182
|
-
<div class="space-y-0">
|
|
183
|
-
<div v-for="(action, i) in actions" :key="i" class="relative">
|
|
184
|
-
<!-- Connector line -->
|
|
185
|
-
<div v-if="i > 0" class="flex justify-center">
|
|
186
|
-
<div class="h-6 w-px bg-[var(--esc-panel-border)]" />
|
|
187
|
-
</div>
|
|
188
|
-
|
|
189
|
-
<!-- Delay divider -->
|
|
190
|
-
<div
|
|
191
|
-
v-if="action.type === 'delay'"
|
|
192
|
-
class="my-1 flex items-center gap-3"
|
|
193
|
-
draggable="true"
|
|
194
|
-
@dragstart="onDragStart(i)"
|
|
195
|
-
@dragover="onDragOver($event, i)"
|
|
196
|
-
@dragend="onDragEnd"
|
|
197
|
-
>
|
|
198
|
-
<div class="h-px flex-1 border-t border-dashed border-orange-500/40" />
|
|
199
|
-
<div
|
|
200
|
-
class="flex items-center gap-2 rounded-full border border-orange-500/30 bg-orange-500/10 px-4 py-1.5"
|
|
201
|
-
>
|
|
202
|
-
<svg
|
|
203
|
-
class="h-4 w-4 text-orange-400"
|
|
204
|
-
fill="none"
|
|
205
|
-
stroke="currentColor"
|
|
206
|
-
stroke-width="1.5"
|
|
207
|
-
viewBox="0 0 24 24"
|
|
208
|
-
>
|
|
209
|
-
<path
|
|
210
|
-
stroke-linecap="round"
|
|
211
|
-
stroke-linejoin="round"
|
|
212
|
-
d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
213
|
-
/>
|
|
214
|
-
</svg>
|
|
215
|
-
<span class="text-xs font-medium text-orange-400">{{ getActionSummary(action) }}</span>
|
|
216
|
-
<button type="button" class="ml-1 text-orange-400/60 hover:text-rose-400" @click="removeAction(i)">
|
|
217
|
-
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
218
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
219
|
-
</svg>
|
|
220
|
-
</button>
|
|
221
|
-
</div>
|
|
222
|
-
<div class="h-px flex-1 border-t border-dashed border-orange-500/40" />
|
|
223
|
-
</div>
|
|
224
|
-
|
|
225
|
-
<!-- Regular action card -->
|
|
226
|
-
<div
|
|
227
|
-
v-else
|
|
228
|
-
class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] transition-all"
|
|
229
|
-
:class="expandedIndex === i ? 'shadow-lg' : ''"
|
|
230
|
-
draggable="true"
|
|
231
|
-
@dragstart="onDragStart(i)"
|
|
232
|
-
@dragover="onDragOver($event, i)"
|
|
233
|
-
@dragend="onDragEnd"
|
|
234
|
-
>
|
|
235
|
-
<!-- Action header -->
|
|
236
|
-
<div class="flex cursor-pointer items-center gap-3 px-4 py-3" @click="toggleExpand(i)">
|
|
237
|
-
<!-- Drag handle -->
|
|
238
|
-
<div class="cursor-grab text-[var(--esc-panel-text-muted)]">
|
|
239
|
-
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
|
240
|
-
<circle cx="9" cy="6" r="1.5" />
|
|
241
|
-
<circle cx="15" cy="6" r="1.5" />
|
|
242
|
-
<circle cx="9" cy="12" r="1.5" />
|
|
243
|
-
<circle cx="15" cy="12" r="1.5" />
|
|
244
|
-
<circle cx="9" cy="18" r="1.5" />
|
|
245
|
-
<circle cx="15" cy="18" r="1.5" />
|
|
246
|
-
</svg>
|
|
247
|
-
</div>
|
|
248
|
-
|
|
249
|
-
<!-- Icon -->
|
|
250
|
-
<div
|
|
251
|
-
class="flex h-8 w-8 items-center justify-center rounded-lg"
|
|
252
|
-
:class="getActionType(action.type).color"
|
|
253
|
-
>
|
|
254
|
-
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
|
255
|
-
<path stroke-linecap="round" stroke-linejoin="round" :d="getActionType(action.type).icon" />
|
|
256
|
-
</svg>
|
|
257
|
-
</div>
|
|
258
|
-
|
|
259
|
-
<!-- Label and summary -->
|
|
260
|
-
<div class="min-w-0 flex-1">
|
|
261
|
-
<div class="text-sm font-medium text-[var(--esc-panel-text-secondary)]">
|
|
262
|
-
{{ getActionType(action.type).label }}
|
|
263
|
-
</div>
|
|
264
|
-
<div class="truncate text-xs text-[var(--esc-panel-text-muted)]">
|
|
265
|
-
{{ getActionSummary(action) }}
|
|
266
|
-
</div>
|
|
267
|
-
</div>
|
|
268
|
-
|
|
269
|
-
<!-- Expand/collapse chevron -->
|
|
270
|
-
<svg
|
|
271
|
-
class="h-4 w-4 text-[var(--esc-panel-text-muted)] transition-transform"
|
|
272
|
-
:class="expandedIndex === i ? 'rotate-180' : ''"
|
|
273
|
-
fill="none"
|
|
274
|
-
stroke="currentColor"
|
|
275
|
-
stroke-width="2"
|
|
276
|
-
viewBox="0 0 24 24"
|
|
277
|
-
>
|
|
278
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
|
279
|
-
</svg>
|
|
280
|
-
|
|
281
|
-
<!-- Remove -->
|
|
282
|
-
<button
|
|
283
|
-
type="button"
|
|
284
|
-
class="flex h-7 w-7 items-center justify-center rounded-lg text-[var(--esc-panel-text-muted)] transition-colors hover:bg-rose-500/10 hover:text-rose-400"
|
|
285
|
-
@click.stop="removeAction(i)"
|
|
286
|
-
>
|
|
287
|
-
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
288
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
289
|
-
</svg>
|
|
290
|
-
</button>
|
|
291
|
-
</div>
|
|
292
|
-
|
|
293
|
-
<!-- Expanded config -->
|
|
294
|
-
<div v-if="expandedIndex === i" class="border-t border-[var(--esc-panel-border)] px-4 pb-4">
|
|
295
|
-
<WorkflowActionConfig
|
|
296
|
-
:action-type="action.type"
|
|
297
|
-
:model-value="action.config"
|
|
298
|
-
@update:model-value="updateConfig(i, $event)"
|
|
299
|
-
/>
|
|
300
|
-
</div>
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
|
|
304
|
-
<!-- Connector to add button -->
|
|
305
|
-
<div v-if="actions.length" class="flex justify-center">
|
|
306
|
-
<div class="h-6 w-px bg-[var(--esc-panel-border)]" />
|
|
307
|
-
</div>
|
|
308
|
-
|
|
309
|
-
<!-- Add action -->
|
|
310
|
-
<div class="relative">
|
|
311
|
-
<button
|
|
312
|
-
type="button"
|
|
313
|
-
class="flex w-full items-center justify-center gap-2 rounded-xl border-2 border-dashed border-[var(--esc-panel-border)] px-4 py-3 text-sm text-[var(--esc-panel-text-muted)] transition-all hover:border-emerald-500/50 hover:text-emerald-400"
|
|
314
|
-
@click="showTypeSelector = !showTypeSelector"
|
|
315
|
-
>
|
|
316
|
-
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
317
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
318
|
-
</svg>
|
|
319
|
-
Add Action
|
|
320
|
-
</button>
|
|
321
|
-
|
|
322
|
-
<!-- Action type selector dropdown -->
|
|
323
|
-
<div
|
|
324
|
-
v-if="showTypeSelector"
|
|
325
|
-
class="absolute left-0 z-20 mt-2 w-full rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] py-2 shadow-2xl"
|
|
326
|
-
>
|
|
327
|
-
<button
|
|
328
|
-
v-for="at in actionTypes"
|
|
329
|
-
:key="at.value"
|
|
330
|
-
type="button"
|
|
331
|
-
class="flex w-full items-center gap-3 px-4 py-2 text-left transition-colors hover:bg-[var(--esc-panel-hover)]"
|
|
332
|
-
@click="addAction(at.value)"
|
|
333
|
-
>
|
|
334
|
-
<div class="flex h-7 w-7 items-center justify-center rounded-lg" :class="at.color">
|
|
335
|
-
<svg
|
|
336
|
-
class="h-3.5 w-3.5"
|
|
337
|
-
fill="none"
|
|
338
|
-
stroke="currentColor"
|
|
339
|
-
stroke-width="1.5"
|
|
340
|
-
viewBox="0 0 24 24"
|
|
341
|
-
>
|
|
342
|
-
<path stroke-linecap="round" stroke-linejoin="round" :d="at.icon" />
|
|
343
|
-
</svg>
|
|
344
|
-
</div>
|
|
345
|
-
<span class="text-sm text-[var(--esc-panel-text-secondary)]">{{ at.label }}</span>
|
|
346
|
-
</button>
|
|
347
|
-
</div>
|
|
348
|
-
</div>
|
|
349
|
-
</div>
|
|
350
|
-
</template>
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from 'vue';
|
|
3
|
+
import WorkflowActionConfig from './WorkflowActionConfig.vue';
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
modelValue: { type: Array, default: () => [] },
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const emit = defineEmits(['update:modelValue']);
|
|
10
|
+
|
|
11
|
+
const actionTypes = [
|
|
12
|
+
{
|
|
13
|
+
value: 'assign_agent',
|
|
14
|
+
label: 'Assign Agent',
|
|
15
|
+
icon: 'M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z',
|
|
16
|
+
color: 'text-blue-400 bg-blue-500/10',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: 'change_status',
|
|
20
|
+
label: 'Change Status',
|
|
21
|
+
icon: 'M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
22
|
+
color: 'text-emerald-400 bg-emerald-500/10',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: 'change_priority',
|
|
26
|
+
label: 'Change Priority',
|
|
27
|
+
icon: 'M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12',
|
|
28
|
+
color: 'text-red-400 bg-red-500/10',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
value: 'add_tag',
|
|
32
|
+
label: 'Add Tag',
|
|
33
|
+
icon: 'M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z',
|
|
34
|
+
color: 'text-violet-400 bg-violet-500/10',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
value: 'remove_tag',
|
|
38
|
+
label: 'Remove Tag',
|
|
39
|
+
icon: 'M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z',
|
|
40
|
+
color: 'text-pink-400 bg-pink-500/10',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
value: 'move_department',
|
|
44
|
+
label: 'Move Department',
|
|
45
|
+
icon: 'M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75z',
|
|
46
|
+
color: 'text-cyan-400 bg-cyan-500/10',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
value: 'add_note',
|
|
50
|
+
label: 'Add Internal Note',
|
|
51
|
+
icon: 'M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931z',
|
|
52
|
+
color: 'text-amber-400 bg-amber-500/10',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
value: 'send_email',
|
|
56
|
+
label: 'Send Email Notification',
|
|
57
|
+
icon: 'M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75',
|
|
58
|
+
color: 'text-sky-400 bg-sky-500/10',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
value: 'send_webhook',
|
|
62
|
+
label: 'Send Webhook',
|
|
63
|
+
icon: 'M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244',
|
|
64
|
+
color: 'text-indigo-400 bg-indigo-500/10',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
value: 'delay',
|
|
68
|
+
label: 'Delay',
|
|
69
|
+
icon: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
70
|
+
color: 'text-orange-400 bg-orange-500/10',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
value: 'apply_macro',
|
|
74
|
+
label: 'Apply Macro',
|
|
75
|
+
icon: 'M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z',
|
|
76
|
+
color: 'text-yellow-400 bg-yellow-500/10',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
value: 'close_ticket',
|
|
80
|
+
label: 'Close Ticket',
|
|
81
|
+
icon: 'M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
82
|
+
color: 'text-gray-400 bg-gray-500/10',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
value: 'snooze',
|
|
86
|
+
label: 'Snooze',
|
|
87
|
+
icon: 'M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0',
|
|
88
|
+
color: 'text-teal-400 bg-teal-500/10',
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const showTypeSelector = ref(false);
|
|
93
|
+
const expandedIndex = ref(null);
|
|
94
|
+
const dragIndex = ref(null);
|
|
95
|
+
|
|
96
|
+
const actions = computed(() => props.modelValue);
|
|
97
|
+
|
|
98
|
+
function getActionType(value) {
|
|
99
|
+
return actionTypes.find((t) => t.value === value) || actionTypes[0];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function addAction(typeValue) {
|
|
103
|
+
const newActions = [...actions.value, { type: typeValue, config: {} }];
|
|
104
|
+
emit('update:modelValue', newActions);
|
|
105
|
+
showTypeSelector.value = false;
|
|
106
|
+
expandedIndex.value = newActions.length - 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function removeAction(index) {
|
|
110
|
+
const newActions = actions.value.filter((_, i) => i !== index);
|
|
111
|
+
emit('update:modelValue', newActions);
|
|
112
|
+
if (expandedIndex.value === index) expandedIndex.value = null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function updateConfig(index, config) {
|
|
116
|
+
const newActions = actions.value.map((a, i) => (i === index ? { ...a, config } : a));
|
|
117
|
+
emit('update:modelValue', newActions);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function toggleExpand(index) {
|
|
121
|
+
expandedIndex.value = expandedIndex.value === index ? null : index;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function getActionSummary(action) {
|
|
125
|
+
const c = action.config || {};
|
|
126
|
+
switch (action.type) {
|
|
127
|
+
case 'assign_agent':
|
|
128
|
+
return c.mode === 'round_robin'
|
|
129
|
+
? 'Round robin'
|
|
130
|
+
: c.mode === 'least_busy'
|
|
131
|
+
? 'Least busy'
|
|
132
|
+
: c.agent || 'Not configured';
|
|
133
|
+
case 'change_status':
|
|
134
|
+
return c.status || 'Not configured';
|
|
135
|
+
case 'change_priority':
|
|
136
|
+
return c.priority || 'Not configured';
|
|
137
|
+
case 'add_tag':
|
|
138
|
+
case 'remove_tag':
|
|
139
|
+
return c.tag || 'Not configured';
|
|
140
|
+
case 'move_department':
|
|
141
|
+
return c.department || 'Not configured';
|
|
142
|
+
case 'add_note':
|
|
143
|
+
return c.body ? c.body.substring(0, 40) + (c.body.length > 40 ? '...' : '') : 'Not configured';
|
|
144
|
+
case 'send_email':
|
|
145
|
+
return c.to || 'Not configured';
|
|
146
|
+
case 'send_webhook':
|
|
147
|
+
return c.url || 'Not configured';
|
|
148
|
+
case 'delay':
|
|
149
|
+
return `Wait ${c.duration || 1} ${c.unit || 'hours'}`;
|
|
150
|
+
case 'apply_macro':
|
|
151
|
+
return c.macro || 'Not configured';
|
|
152
|
+
case 'close_ticket':
|
|
153
|
+
return 'Close immediately';
|
|
154
|
+
case 'snooze':
|
|
155
|
+
return `${c.duration || 1} ${c.unit || 'hours'}`;
|
|
156
|
+
default:
|
|
157
|
+
return 'Not configured';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Drag-and-drop reordering
|
|
162
|
+
function onDragStart(index) {
|
|
163
|
+
dragIndex.value = index;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function onDragOver(event, index) {
|
|
167
|
+
event.preventDefault();
|
|
168
|
+
if (dragIndex.value === null || dragIndex.value === index) return;
|
|
169
|
+
const newActions = [...actions.value];
|
|
170
|
+
const [moved] = newActions.splice(dragIndex.value, 1);
|
|
171
|
+
newActions.splice(index, 0, moved);
|
|
172
|
+
emit('update:modelValue', newActions);
|
|
173
|
+
dragIndex.value = index;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function onDragEnd() {
|
|
177
|
+
dragIndex.value = null;
|
|
178
|
+
}
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<template>
|
|
182
|
+
<div class="space-y-0">
|
|
183
|
+
<div v-for="(action, i) in actions" :key="i" class="relative">
|
|
184
|
+
<!-- Connector line -->
|
|
185
|
+
<div v-if="i > 0" class="flex justify-center">
|
|
186
|
+
<div class="h-6 w-px bg-[var(--esc-panel-border)]" />
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<!-- Delay divider -->
|
|
190
|
+
<div
|
|
191
|
+
v-if="action.type === 'delay'"
|
|
192
|
+
class="my-1 flex items-center gap-3"
|
|
193
|
+
draggable="true"
|
|
194
|
+
@dragstart="onDragStart(i)"
|
|
195
|
+
@dragover="onDragOver($event, i)"
|
|
196
|
+
@dragend="onDragEnd"
|
|
197
|
+
>
|
|
198
|
+
<div class="h-px flex-1 border-t border-dashed border-orange-500/40" />
|
|
199
|
+
<div
|
|
200
|
+
class="flex items-center gap-2 rounded-full border border-orange-500/30 bg-orange-500/10 px-4 py-1.5"
|
|
201
|
+
>
|
|
202
|
+
<svg
|
|
203
|
+
class="h-4 w-4 text-orange-400"
|
|
204
|
+
fill="none"
|
|
205
|
+
stroke="currentColor"
|
|
206
|
+
stroke-width="1.5"
|
|
207
|
+
viewBox="0 0 24 24"
|
|
208
|
+
>
|
|
209
|
+
<path
|
|
210
|
+
stroke-linecap="round"
|
|
211
|
+
stroke-linejoin="round"
|
|
212
|
+
d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
213
|
+
/>
|
|
214
|
+
</svg>
|
|
215
|
+
<span class="text-xs font-medium text-orange-400">{{ getActionSummary(action) }}</span>
|
|
216
|
+
<button type="button" class="ml-1 text-orange-400/60 hover:text-rose-400" @click="removeAction(i)">
|
|
217
|
+
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
218
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
219
|
+
</svg>
|
|
220
|
+
</button>
|
|
221
|
+
</div>
|
|
222
|
+
<div class="h-px flex-1 border-t border-dashed border-orange-500/40" />
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<!-- Regular action card -->
|
|
226
|
+
<div
|
|
227
|
+
v-else
|
|
228
|
+
class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] transition-all"
|
|
229
|
+
:class="expandedIndex === i ? 'shadow-lg' : ''"
|
|
230
|
+
draggable="true"
|
|
231
|
+
@dragstart="onDragStart(i)"
|
|
232
|
+
@dragover="onDragOver($event, i)"
|
|
233
|
+
@dragend="onDragEnd"
|
|
234
|
+
>
|
|
235
|
+
<!-- Action header -->
|
|
236
|
+
<div class="flex cursor-pointer items-center gap-3 px-4 py-3" @click="toggleExpand(i)">
|
|
237
|
+
<!-- Drag handle -->
|
|
238
|
+
<div class="cursor-grab text-[var(--esc-panel-text-muted)]">
|
|
239
|
+
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
|
240
|
+
<circle cx="9" cy="6" r="1.5" />
|
|
241
|
+
<circle cx="15" cy="6" r="1.5" />
|
|
242
|
+
<circle cx="9" cy="12" r="1.5" />
|
|
243
|
+
<circle cx="15" cy="12" r="1.5" />
|
|
244
|
+
<circle cx="9" cy="18" r="1.5" />
|
|
245
|
+
<circle cx="15" cy="18" r="1.5" />
|
|
246
|
+
</svg>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<!-- Icon -->
|
|
250
|
+
<div
|
|
251
|
+
class="flex h-8 w-8 items-center justify-center rounded-lg"
|
|
252
|
+
:class="getActionType(action.type).color"
|
|
253
|
+
>
|
|
254
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
|
255
|
+
<path stroke-linecap="round" stroke-linejoin="round" :d="getActionType(action.type).icon" />
|
|
256
|
+
</svg>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<!-- Label and summary -->
|
|
260
|
+
<div class="min-w-0 flex-1">
|
|
261
|
+
<div class="text-sm font-medium text-[var(--esc-panel-text-secondary)]">
|
|
262
|
+
{{ getActionType(action.type).label }}
|
|
263
|
+
</div>
|
|
264
|
+
<div class="truncate text-xs text-[var(--esc-panel-text-muted)]">
|
|
265
|
+
{{ getActionSummary(action) }}
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<!-- Expand/collapse chevron -->
|
|
270
|
+
<svg
|
|
271
|
+
class="h-4 w-4 text-[var(--esc-panel-text-muted)] transition-transform"
|
|
272
|
+
:class="expandedIndex === i ? 'rotate-180' : ''"
|
|
273
|
+
fill="none"
|
|
274
|
+
stroke="currentColor"
|
|
275
|
+
stroke-width="2"
|
|
276
|
+
viewBox="0 0 24 24"
|
|
277
|
+
>
|
|
278
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
|
279
|
+
</svg>
|
|
280
|
+
|
|
281
|
+
<!-- Remove -->
|
|
282
|
+
<button
|
|
283
|
+
type="button"
|
|
284
|
+
class="flex h-7 w-7 items-center justify-center rounded-lg text-[var(--esc-panel-text-muted)] transition-colors hover:bg-rose-500/10 hover:text-rose-400"
|
|
285
|
+
@click.stop="removeAction(i)"
|
|
286
|
+
>
|
|
287
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
288
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
289
|
+
</svg>
|
|
290
|
+
</button>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<!-- Expanded config -->
|
|
294
|
+
<div v-if="expandedIndex === i" class="border-t border-[var(--esc-panel-border)] px-4 pb-4">
|
|
295
|
+
<WorkflowActionConfig
|
|
296
|
+
:action-type="action.type"
|
|
297
|
+
:model-value="action.config"
|
|
298
|
+
@update:model-value="updateConfig(i, $event)"
|
|
299
|
+
/>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<!-- Connector to add button -->
|
|
305
|
+
<div v-if="actions.length" class="flex justify-center">
|
|
306
|
+
<div class="h-6 w-px bg-[var(--esc-panel-border)]" />
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<!-- Add action -->
|
|
310
|
+
<div class="relative">
|
|
311
|
+
<button
|
|
312
|
+
type="button"
|
|
313
|
+
class="flex w-full items-center justify-center gap-2 rounded-xl border-2 border-dashed border-[var(--esc-panel-border)] px-4 py-3 text-sm text-[var(--esc-panel-text-muted)] transition-all hover:border-emerald-500/50 hover:text-emerald-400"
|
|
314
|
+
@click="showTypeSelector = !showTypeSelector"
|
|
315
|
+
>
|
|
316
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
317
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
318
|
+
</svg>
|
|
319
|
+
Add Action
|
|
320
|
+
</button>
|
|
321
|
+
|
|
322
|
+
<!-- Action type selector dropdown -->
|
|
323
|
+
<div
|
|
324
|
+
v-if="showTypeSelector"
|
|
325
|
+
class="absolute left-0 z-20 mt-2 w-full rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] py-2 shadow-2xl"
|
|
326
|
+
>
|
|
327
|
+
<button
|
|
328
|
+
v-for="at in actionTypes"
|
|
329
|
+
:key="at.value"
|
|
330
|
+
type="button"
|
|
331
|
+
class="flex w-full items-center gap-3 px-4 py-2 text-left transition-colors hover:bg-[var(--esc-panel-hover)]"
|
|
332
|
+
@click="addAction(at.value)"
|
|
333
|
+
>
|
|
334
|
+
<div class="flex h-7 w-7 items-center justify-center rounded-lg" :class="at.color">
|
|
335
|
+
<svg
|
|
336
|
+
class="h-3.5 w-3.5"
|
|
337
|
+
fill="none"
|
|
338
|
+
stroke="currentColor"
|
|
339
|
+
stroke-width="1.5"
|
|
340
|
+
viewBox="0 0 24 24"
|
|
341
|
+
>
|
|
342
|
+
<path stroke-linecap="round" stroke-linejoin="round" :d="at.icon" />
|
|
343
|
+
</svg>
|
|
344
|
+
</div>
|
|
345
|
+
<span class="text-sm text-[var(--esc-panel-text-secondary)]">{{ at.label }}</span>
|
|
346
|
+
</button>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
</template>
|