@escalated-dev/escalated 0.7.0 → 0.7.1
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 +31 -0
- package/package.json +12 -6
- package/src/components/ActiveChatsPanel.vue +246 -0
- package/src/components/ActivityTimeline.stories.js +55 -55
- package/src/components/ActivityTimeline.vue +83 -83
- package/src/components/AdminDashboard.stories.js +1423 -1095
- package/src/components/AgentLoadIndicator.stories.js +37 -37
- package/src/components/AgentLoadIndicator.vue +49 -49
- package/src/components/AgentPerformanceCard.vue +44 -44
- package/src/components/ArticleEditor.vue +82 -82
- package/src/components/AssigneeSelect.vue +37 -37
- package/src/components/AttachmentList.vue +13 -1
- package/src/components/AuditLogEntry.vue +95 -95
- package/src/components/BulkActionBar.vue +166 -166
- package/src/components/CategoryTree.vue +89 -89
- package/src/components/ChartWidget.vue +46 -46
- package/src/components/ChatActionBar.vue +226 -0
- package/src/components/ChatBubble.vue +162 -0
- package/src/components/ChatComposer.vue +308 -0
- package/src/components/ChatQueue.vue +222 -0
- package/src/components/ChatThread.vue +197 -0
- package/src/components/CollisionWarning.stories.js +30 -30
- package/src/components/CollisionWarning.vue +68 -68
- package/src/components/ConditionalFieldRules.vue +135 -135
- package/src/components/ContextPanel.vue +66 -66
- package/src/components/ContextPanelSection.vue +65 -65
- package/src/components/CustomFieldRenderer.vue +159 -159
- package/src/components/EscalatedLayout.vue +29 -6
- package/src/components/FileDropzone.vue +57 -57
- package/src/components/FollowButton.stories.js +30 -30
- package/src/components/FollowButton.vue +88 -88
- package/src/components/KeyboardShortcutHelp.vue +213 -213
- package/src/components/KnowledgePanel.vue +122 -122
- package/src/components/KpiCard.stories.js +32 -32
- package/src/components/KpiCard.vue +88 -88
- package/src/components/MacroDropdown.vue +150 -150
- package/src/components/MentionDropdown.vue +154 -0
- package/src/components/MetricComparison.vue +89 -0
- package/src/components/PercentileChart.vue +67 -0
- package/src/components/PeriodSelector.vue +98 -0
- package/src/components/PermissionMatrix.vue +107 -107
- package/src/components/PinnedNotes.vue +129 -129
- package/src/components/PluginSlot.vue +68 -62
- package/src/components/PresenceIndicator.vue +46 -2
- package/src/components/PriorityBadge.stories.js +33 -33
- package/src/components/PriorityBadge.vue +46 -46
- package/src/components/ReplyComposer.vue +81 -18
- package/src/components/ReplyThread.vue +40 -2
- package/src/components/ReportChart.vue +395 -0
- package/src/components/ReportExportButton.vue +108 -0
- package/src/components/SatisfactionRating.vue +180 -180
- package/src/components/SaveViewDialog.vue +172 -0
- package/src/components/SavedViewSidebar.vue +208 -0
- package/src/components/ScheduleEditor.vue +104 -104
- package/src/components/SideConversation.vue +165 -165
- package/src/components/SideConversationList.vue +147 -147
- package/src/components/SkillTagManager.vue +79 -79
- package/src/components/SlaComplianceChart.vue +46 -53
- package/src/components/SlaTimer.stories.js +35 -35
- package/src/components/SlaTimer.vue +62 -62
- package/src/components/SnoozeButton.vue +247 -0
- package/src/components/StatsCard.stories.js +24 -24
- package/src/components/StatsCard.vue +58 -58
- package/src/components/StatusBadge.stories.js +53 -53
- package/src/components/StatusBadge.vue +78 -78
- package/src/components/TagSelect.vue +84 -84
- package/src/components/TicketFilters.vue +270 -225
- package/src/components/TicketLinkPanel.vue +193 -193
- package/src/components/TicketList.vue +655 -594
- package/src/components/TicketMergeDialog.vue +157 -157
- package/src/components/TicketSplitDialog.vue +98 -0
- package/src/components/TotpInput.stories.js +9 -9
- package/src/components/TotpInput.vue +81 -81
- package/src/components/TwoFactorSetup.vue +113 -113
- package/src/components/VariableTokenInput.vue +111 -0
- package/src/components/WorkflowActionConfig.vue +287 -0
- package/src/components/WorkflowActionList.vue +350 -0
- package/src/components/WorkflowConditionBuilder.vue +265 -0
- package/src/components/WorkflowTriggerSelector.vue +125 -0
- package/src/composables/useChat.js +187 -0
- package/src/composables/useI18n.js +80 -80
- package/src/composables/useKeyboardShortcuts.js +46 -46
- package/src/composables/useMentions.js +134 -0
- package/src/composables/usePluginExtensions.js +159 -159
- package/src/composables/usePluginHooks.js +65 -65
- package/src/composables/useRealtime.js +182 -0
- package/src/defineEscalatedPlugin.js +76 -76
- package/src/index.js +84 -72
- package/src/locales/ar.json +727 -0
- package/src/locales/de.json +678 -430
- package/src/locales/en.json +717 -455
- package/src/locales/es.json +679 -430
- package/src/locales/fr.json +680 -431
- package/src/locales/index.js +12 -2
- package/src/locales/it.json +727 -0
- package/src/locales/ja.json +727 -0
- package/src/locales/ko.json +727 -0
- package/src/locales/nl.json +727 -0
- package/src/locales/pl.json +727 -0
- package/src/locales/pt-BR.json +727 -0
- package/src/locales/ru.json +727 -0
- package/src/locales/tr.json +727 -0
- package/src/locales/zh-CN.json +727 -0
- package/src/pages/Admin/ApiTokens/Index.vue +381 -381
- package/src/pages/Admin/AuditLog/Index.vue +154 -154
- package/src/pages/Admin/BusinessHours/Form.vue +172 -172
- package/src/pages/Admin/BusinessHours/Index.vue +113 -113
- package/src/pages/Admin/CannedResponses/Index.vue +127 -127
- package/src/pages/Admin/Capacity/Index.vue +134 -134
- package/src/pages/Admin/CustomFields/Form.vue +195 -195
- package/src/pages/Admin/CustomFields/Index.vue +134 -134
- package/src/pages/Admin/CustomObjects/Form.vue +202 -202
- package/src/pages/Admin/CustomObjects/Index.vue +127 -127
- package/src/pages/Admin/CustomObjects/Records.vue +228 -228
- package/src/pages/Admin/Departments/Form.vue +75 -75
- package/src/pages/Admin/Departments/Index.vue +110 -109
- package/src/pages/Admin/EscalationRules/Form.vue +157 -157
- package/src/pages/Admin/EscalationRules/Index.vue +178 -178
- package/src/pages/Admin/Import/Connect.vue +0 -1
- package/src/pages/Admin/Import/Index.vue +1 -1
- package/src/pages/Admin/Import/Progress.vue +1 -1
- package/src/pages/Admin/KnowledgeBase/Articles/Form.vue +142 -142
- package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +195 -195
- package/src/pages/Admin/KnowledgeBase/Categories/Index.vue +193 -193
- package/src/pages/Admin/Macros/Index.vue +448 -448
- package/src/pages/Admin/Plugins/Index.vue +327 -327
- package/src/pages/Admin/Reports/AgentDetail.vue +132 -0
- package/src/pages/Admin/Reports/AgentMetrics.vue +115 -115
- package/src/pages/Admin/Reports/AgentRanking.vue +246 -0
- package/src/pages/Admin/Reports/Cohorts.vue +180 -0
- package/src/pages/Admin/Reports/Comparison.vue +141 -0
- package/src/pages/Admin/Reports/CsatReport.vue +127 -127
- package/src/pages/Admin/Reports/Dashboard.vue +36 -21
- package/src/pages/Admin/Reports/ResolutionTimes.vue +164 -0
- package/src/pages/Admin/Reports/ResponseTimes.vue +164 -0
- package/src/pages/Admin/Reports/SlaReport.vue +162 -166
- package/src/pages/Admin/Reports/SlaTrends.vue +183 -0
- package/src/pages/Admin/Reports.vue +206 -206
- package/src/pages/Admin/Roles/Form.vue +71 -71
- package/src/pages/Admin/Roles/Index.vue +122 -122
- package/src/pages/Admin/Settings/CsatSettings.vue +165 -165
- package/src/pages/Admin/Settings/DataRetention.vue +182 -182
- package/src/pages/Admin/Settings/EmailSettings.vue +243 -243
- package/src/pages/Admin/Settings/Sandbox.vue +135 -135
- package/src/pages/Admin/Settings/SsoSettings.vue +218 -218
- package/src/pages/Admin/Settings/TwoFactor.vue +110 -110
- package/src/pages/Admin/Settings.vue +1008 -468
- package/src/pages/Admin/Skills/Form.vue +53 -53
- package/src/pages/Admin/Skills/Index.vue +94 -94
- package/src/pages/Admin/SlaPolicies/Form.vue +118 -118
- package/src/pages/Admin/SlaPolicies/Index.vue +121 -120
- package/src/pages/Admin/Statuses/Form.vue +114 -114
- package/src/pages/Admin/Statuses/Index.vue +153 -153
- package/src/pages/Admin/Tags/Index.vue +191 -191
- package/src/pages/Admin/Tickets/Index.vue +126 -91
- package/src/pages/Admin/Tickets/Show.vue +29 -0
- package/src/pages/Admin/Webhooks/DeliveryLog.vue +120 -120
- package/src/pages/Admin/Webhooks/Form.vue +110 -110
- package/src/pages/Admin/Webhooks/Index.vue +136 -136
- package/src/pages/Admin/Workflows/Builder.vue +207 -0
- package/src/pages/Admin/Workflows/Index.vue +235 -0
- package/src/pages/Admin/Workflows/Logs.vue +286 -0
- package/src/pages/Agent/TicketIndex.vue +112 -88
- package/src/pages/Agent/TicketShow.vue +192 -60
- package/src/pages/Customer/Create.vue +1 -1
- package/src/pages/Customer/Index.vue +31 -31
- package/src/pages/Customer/KnowledgeBase/Article.vue +4 -2
- package/src/pages/Customer/KnowledgeBase/Index.vue +151 -151
- package/src/pages/Customer/Show.vue +69 -63
- package/src/pages/Guest/Create.vue +40 -15
- package/src/pages/Guest/Show.vue +124 -93
- package/src/plugin.js +25 -0
- package/src/utils/mentionHighlight.js +24 -0
- package/src/utils/sanitizeHtml.js +78 -64
- package/src/widget/EscalatedWidget.vue +804 -0
- package/src/widget/index.js +254 -0
- package/src/pages/Admin/Automations/Form.vue +0 -184
- package/src/pages/Admin/Automations/Index.vue +0 -129
|
@@ -1,243 +1,243 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import EscalatedLayout from '../../../components/EscalatedLayout.vue';
|
|
3
|
-
import { useForm } from '@inertiajs/vue3';
|
|
4
|
-
import { ref } from 'vue';
|
|
5
|
-
|
|
6
|
-
const props = defineProps({
|
|
7
|
-
addresses: { type: Array, default: () => [] },
|
|
8
|
-
defaultReplyAddress: { type: String, default: '' },
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const form = useForm({
|
|
12
|
-
addresses: props.addresses.map((a) => ({ ...a })),
|
|
13
|
-
default_reply_address: props.defaultReplyAddress,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const showAddForm = ref(false);
|
|
17
|
-
const newAddress = ref({
|
|
18
|
-
email: '',
|
|
19
|
-
display_name: '',
|
|
20
|
-
department_id: null,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
function addAddress() {
|
|
24
|
-
form.addresses.push({
|
|
25
|
-
email: newAddress.value.email,
|
|
26
|
-
display_name: newAddress.value.display_name,
|
|
27
|
-
department_id: newAddress.value.department_id,
|
|
28
|
-
dkim_status: 'unknown',
|
|
29
|
-
});
|
|
30
|
-
newAddress.value = { email: '', display_name: '', department_id: null };
|
|
31
|
-
showAddForm.value = false;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function removeAddress(index) {
|
|
35
|
-
form.addresses.splice(index, 1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function submit() {
|
|
39
|
-
form.post(route('escalated.admin.settings.email.update'));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const dkimStatusClasses = {
|
|
43
|
-
verified: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',
|
|
44
|
-
pending: 'bg-amber-500/10 text-amber-400 border-amber-500/20',
|
|
45
|
-
failed: 'bg-rose-500/10 text-rose-400 border-rose-500/20',
|
|
46
|
-
unknown: 'bg-neutral-500/10 text-neutral-400 border-neutral-500/20',
|
|
47
|
-
};
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<template>
|
|
51
|
-
<EscalatedLayout title="Email Channel Settings">
|
|
52
|
-
<form class="mx-auto max-w-3xl space-y-6" @submit.prevent="submit">
|
|
53
|
-
<!-- Email Addresses -->
|
|
54
|
-
<div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
|
|
55
|
-
<div class="mb-5 flex items-center justify-between">
|
|
56
|
-
<div>
|
|
57
|
-
<h3 class="text-sm font-semibold text-[var(--esc-panel-text)]">Support Email Addresses</h3>
|
|
58
|
-
<p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
|
|
59
|
-
Configure multiple email addresses for receiving and responding to tickets
|
|
60
|
-
</p>
|
|
61
|
-
</div>
|
|
62
|
-
<button
|
|
63
|
-
type="button"
|
|
64
|
-
class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-1.5 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)]"
|
|
65
|
-
@click="showAddForm = true"
|
|
66
|
-
>
|
|
67
|
-
Add Address
|
|
68
|
-
</button>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<!-- Address Table -->
|
|
72
|
-
<div
|
|
73
|
-
v-if="form.addresses.length"
|
|
74
|
-
class="overflow-hidden rounded-lg border border-[var(--esc-panel-border)]"
|
|
75
|
-
>
|
|
76
|
-
<table class="w-full">
|
|
77
|
-
<thead>
|
|
78
|
-
<tr class="border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-hover)]">
|
|
79
|
-
<th
|
|
80
|
-
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
81
|
-
>
|
|
82
|
-
Email
|
|
83
|
-
</th>
|
|
84
|
-
<th
|
|
85
|
-
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
86
|
-
>
|
|
87
|
-
Display Name
|
|
88
|
-
</th>
|
|
89
|
-
<th
|
|
90
|
-
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
91
|
-
>
|
|
92
|
-
Department
|
|
93
|
-
</th>
|
|
94
|
-
<th
|
|
95
|
-
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
96
|
-
>
|
|
97
|
-
DKIM
|
|
98
|
-
</th>
|
|
99
|
-
<th
|
|
100
|
-
class="px-4 py-3 text-right text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
101
|
-
>
|
|
102
|
-
Actions
|
|
103
|
-
</th>
|
|
104
|
-
</tr>
|
|
105
|
-
</thead>
|
|
106
|
-
<tbody class="divide-y divide-[var(--esc-panel-border)]">
|
|
107
|
-
<tr
|
|
108
|
-
v-for="(addr, idx) in form.addresses"
|
|
109
|
-
:key="idx"
|
|
110
|
-
class="hover:bg-[var(--esc-panel-hover)]"
|
|
111
|
-
>
|
|
112
|
-
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-secondary)]">
|
|
113
|
-
{{ addr.email }}
|
|
114
|
-
</td>
|
|
115
|
-
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
|
|
116
|
-
{{ addr.display_name || '---' }}
|
|
117
|
-
</td>
|
|
118
|
-
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
|
|
119
|
-
{{ addr.department_id ? `Dept #${addr.department_id}` : 'All' }}
|
|
120
|
-
</td>
|
|
121
|
-
<td class="px-4 py-3">
|
|
122
|
-
<span
|
|
123
|
-
:class="[
|
|
124
|
-
'inline-flex rounded-full border px-2 py-0.5 text-xs font-medium',
|
|
125
|
-
dkimStatusClasses[addr.dkim_status] || dkimStatusClasses.unknown,
|
|
126
|
-
]"
|
|
127
|
-
>
|
|
128
|
-
{{ addr.dkim_status || 'Unknown' }}
|
|
129
|
-
</span>
|
|
130
|
-
</td>
|
|
131
|
-
<td class="px-4 py-3 text-right">
|
|
132
|
-
<button
|
|
133
|
-
type="button"
|
|
134
|
-
class="text-sm text-red-500/60 hover:text-red-400"
|
|
135
|
-
@click="removeAddress(idx)"
|
|
136
|
-
>
|
|
137
|
-
Remove
|
|
138
|
-
</button>
|
|
139
|
-
</td>
|
|
140
|
-
</tr>
|
|
141
|
-
</tbody>
|
|
142
|
-
</table>
|
|
143
|
-
</div>
|
|
144
|
-
|
|
145
|
-
<div
|
|
146
|
-
v-else
|
|
147
|
-
class="rounded-lg border border-dashed border-[var(--esc-panel-active)] px-6 py-8 text-center"
|
|
148
|
-
>
|
|
149
|
-
<p class="text-sm text-[var(--esc-panel-text-muted)]">No email addresses configured yet.</p>
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
<!-- Add Address Form -->
|
|
153
|
-
<div
|
|
154
|
-
v-if="showAddForm"
|
|
155
|
-
class="mt-4 rounded-lg border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface-alt)] p-4"
|
|
156
|
-
>
|
|
157
|
-
<h4 class="mb-3 text-sm font-medium text-[var(--esc-panel-text-secondary)]">Add Email Address</h4>
|
|
158
|
-
<div class="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
|
159
|
-
<div>
|
|
160
|
-
<label class="block text-xs font-medium text-[var(--esc-panel-text-tertiary)]"
|
|
161
|
-
>Email Address</label
|
|
162
|
-
>
|
|
163
|
-
<input
|
|
164
|
-
v-model="newAddress.email"
|
|
165
|
-
type="email"
|
|
166
|
-
placeholder="support@example.com"
|
|
167
|
-
class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
168
|
-
/>
|
|
169
|
-
</div>
|
|
170
|
-
<div>
|
|
171
|
-
<label class="block text-xs font-medium text-[var(--esc-panel-text-tertiary)]"
|
|
172
|
-
>Display Name</label
|
|
173
|
-
>
|
|
174
|
-
<input
|
|
175
|
-
v-model="newAddress.display_name"
|
|
176
|
-
type="text"
|
|
177
|
-
placeholder="Support Team"
|
|
178
|
-
class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
179
|
-
/>
|
|
180
|
-
</div>
|
|
181
|
-
<div>
|
|
182
|
-
<label class="block text-xs font-medium text-[var(--esc-panel-text-tertiary)]"
|
|
183
|
-
>Department Routing</label
|
|
184
|
-
>
|
|
185
|
-
<input
|
|
186
|
-
v-model.number="newAddress.department_id"
|
|
187
|
-
type="number"
|
|
188
|
-
placeholder="All departments"
|
|
189
|
-
class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
190
|
-
/>
|
|
191
|
-
</div>
|
|
192
|
-
</div>
|
|
193
|
-
<div class="mt-3 flex gap-2">
|
|
194
|
-
<button
|
|
195
|
-
type="button"
|
|
196
|
-
class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-1.5 text-sm font-medium text-white"
|
|
197
|
-
:disabled="!newAddress.email"
|
|
198
|
-
@click="addAddress"
|
|
199
|
-
>
|
|
200
|
-
Add
|
|
201
|
-
</button>
|
|
202
|
-
<button
|
|
203
|
-
type="button"
|
|
204
|
-
class="rounded-lg border border-[var(--esc-panel-border-input)] px-4 py-1.5 text-sm text-[var(--esc-panel-text-tertiary)] hover:text-[var(--esc-panel-text-secondary)]"
|
|
205
|
-
@click="showAddForm = false"
|
|
206
|
-
>
|
|
207
|
-
Cancel
|
|
208
|
-
</button>
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
|
|
213
|
-
<!-- Default Reply Address -->
|
|
214
|
-
<div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
|
|
215
|
-
<h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Default Reply Address</h3>
|
|
216
|
-
<p class="mb-3 text-xs text-[var(--esc-panel-text-muted)]">
|
|
217
|
-
Select the default email address for outbound replies
|
|
218
|
-
</p>
|
|
219
|
-
<select
|
|
220
|
-
v-model="form.default_reply_address"
|
|
221
|
-
class="w-full max-w-sm rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
222
|
-
>
|
|
223
|
-
<option value="">Select an address...</option>
|
|
224
|
-
<option v-for="(addr, idx) in form.addresses" :key="idx" :value="addr.email">
|
|
225
|
-
{{ addr.display_name ? `${addr.display_name} <${addr.email}>` : addr.email }}
|
|
226
|
-
</option>
|
|
227
|
-
</select>
|
|
228
|
-
</div>
|
|
229
|
-
|
|
230
|
-
<!-- Submit -->
|
|
231
|
-
<div class="flex items-center justify-end gap-3">
|
|
232
|
-
<span v-if="form.recentlySuccessful" class="text-sm text-emerald-400">Saved.</span>
|
|
233
|
-
<button
|
|
234
|
-
type="submit"
|
|
235
|
-
:disabled="form.processing"
|
|
236
|
-
class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-5 py-2 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)] disabled:opacity-50"
|
|
237
|
-
>
|
|
238
|
-
{{ form.processing ? 'Saving...' : 'Save Email Settings' }}
|
|
239
|
-
</button>
|
|
240
|
-
</div>
|
|
241
|
-
</form>
|
|
242
|
-
</EscalatedLayout>
|
|
243
|
-
</template>
|
|
1
|
+
<script setup>
|
|
2
|
+
import EscalatedLayout from '../../../components/EscalatedLayout.vue';
|
|
3
|
+
import { useForm } from '@inertiajs/vue3';
|
|
4
|
+
import { ref } from 'vue';
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
addresses: { type: Array, default: () => [] },
|
|
8
|
+
defaultReplyAddress: { type: String, default: '' },
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const form = useForm({
|
|
12
|
+
addresses: props.addresses.map((a) => ({ ...a })),
|
|
13
|
+
default_reply_address: props.defaultReplyAddress,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const showAddForm = ref(false);
|
|
17
|
+
const newAddress = ref({
|
|
18
|
+
email: '',
|
|
19
|
+
display_name: '',
|
|
20
|
+
department_id: null,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function addAddress() {
|
|
24
|
+
form.addresses.push({
|
|
25
|
+
email: newAddress.value.email,
|
|
26
|
+
display_name: newAddress.value.display_name,
|
|
27
|
+
department_id: newAddress.value.department_id,
|
|
28
|
+
dkim_status: 'unknown',
|
|
29
|
+
});
|
|
30
|
+
newAddress.value = { email: '', display_name: '', department_id: null };
|
|
31
|
+
showAddForm.value = false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function removeAddress(index) {
|
|
35
|
+
form.addresses.splice(index, 1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function submit() {
|
|
39
|
+
form.post(route('escalated.admin.settings.email.update'));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dkimStatusClasses = {
|
|
43
|
+
verified: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',
|
|
44
|
+
pending: 'bg-amber-500/10 text-amber-400 border-amber-500/20',
|
|
45
|
+
failed: 'bg-rose-500/10 text-rose-400 border-rose-500/20',
|
|
46
|
+
unknown: 'bg-neutral-500/10 text-neutral-400 border-neutral-500/20',
|
|
47
|
+
};
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<EscalatedLayout title="Email Channel Settings">
|
|
52
|
+
<form class="mx-auto max-w-3xl space-y-6" @submit.prevent="submit">
|
|
53
|
+
<!-- Email Addresses -->
|
|
54
|
+
<div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
|
|
55
|
+
<div class="mb-5 flex items-center justify-between">
|
|
56
|
+
<div>
|
|
57
|
+
<h3 class="text-sm font-semibold text-[var(--esc-panel-text)]">Support Email Addresses</h3>
|
|
58
|
+
<p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
|
|
59
|
+
Configure multiple email addresses for receiving and responding to tickets
|
|
60
|
+
</p>
|
|
61
|
+
</div>
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-1.5 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)]"
|
|
65
|
+
@click="showAddForm = true"
|
|
66
|
+
>
|
|
67
|
+
Add Address
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- Address Table -->
|
|
72
|
+
<div
|
|
73
|
+
v-if="form.addresses.length"
|
|
74
|
+
class="overflow-hidden rounded-lg border border-[var(--esc-panel-border)]"
|
|
75
|
+
>
|
|
76
|
+
<table class="w-full">
|
|
77
|
+
<thead>
|
|
78
|
+
<tr class="border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-hover)]">
|
|
79
|
+
<th
|
|
80
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
81
|
+
>
|
|
82
|
+
Email
|
|
83
|
+
</th>
|
|
84
|
+
<th
|
|
85
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
86
|
+
>
|
|
87
|
+
Display Name
|
|
88
|
+
</th>
|
|
89
|
+
<th
|
|
90
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
91
|
+
>
|
|
92
|
+
Department
|
|
93
|
+
</th>
|
|
94
|
+
<th
|
|
95
|
+
class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
96
|
+
>
|
|
97
|
+
DKIM
|
|
98
|
+
</th>
|
|
99
|
+
<th
|
|
100
|
+
class="px-4 py-3 text-right text-xs font-medium uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
|
|
101
|
+
>
|
|
102
|
+
Actions
|
|
103
|
+
</th>
|
|
104
|
+
</tr>
|
|
105
|
+
</thead>
|
|
106
|
+
<tbody class="divide-y divide-[var(--esc-panel-border)]">
|
|
107
|
+
<tr
|
|
108
|
+
v-for="(addr, idx) in form.addresses"
|
|
109
|
+
:key="idx"
|
|
110
|
+
class="hover:bg-[var(--esc-panel-hover)]"
|
|
111
|
+
>
|
|
112
|
+
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-secondary)]">
|
|
113
|
+
{{ addr.email }}
|
|
114
|
+
</td>
|
|
115
|
+
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
|
|
116
|
+
{{ addr.display_name || '---' }}
|
|
117
|
+
</td>
|
|
118
|
+
<td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
|
|
119
|
+
{{ addr.department_id ? `Dept #${addr.department_id}` : 'All' }}
|
|
120
|
+
</td>
|
|
121
|
+
<td class="px-4 py-3">
|
|
122
|
+
<span
|
|
123
|
+
:class="[
|
|
124
|
+
'inline-flex rounded-full border px-2 py-0.5 text-xs font-medium',
|
|
125
|
+
dkimStatusClasses[addr.dkim_status] || dkimStatusClasses.unknown,
|
|
126
|
+
]"
|
|
127
|
+
>
|
|
128
|
+
{{ addr.dkim_status || 'Unknown' }}
|
|
129
|
+
</span>
|
|
130
|
+
</td>
|
|
131
|
+
<td class="px-4 py-3 text-right">
|
|
132
|
+
<button
|
|
133
|
+
type="button"
|
|
134
|
+
class="text-sm text-red-500/60 hover:text-red-400"
|
|
135
|
+
@click="removeAddress(idx)"
|
|
136
|
+
>
|
|
137
|
+
Remove
|
|
138
|
+
</button>
|
|
139
|
+
</td>
|
|
140
|
+
</tr>
|
|
141
|
+
</tbody>
|
|
142
|
+
</table>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div
|
|
146
|
+
v-else
|
|
147
|
+
class="rounded-lg border border-dashed border-[var(--esc-panel-active)] px-6 py-8 text-center"
|
|
148
|
+
>
|
|
149
|
+
<p class="text-sm text-[var(--esc-panel-text-muted)]">No email addresses configured yet.</p>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<!-- Add Address Form -->
|
|
153
|
+
<div
|
|
154
|
+
v-if="showAddForm"
|
|
155
|
+
class="mt-4 rounded-lg border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface-alt)] p-4"
|
|
156
|
+
>
|
|
157
|
+
<h4 class="mb-3 text-sm font-medium text-[var(--esc-panel-text-secondary)]">Add Email Address</h4>
|
|
158
|
+
<div class="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
|
159
|
+
<div>
|
|
160
|
+
<label class="block text-xs font-medium text-[var(--esc-panel-text-tertiary)]"
|
|
161
|
+
>Email Address</label
|
|
162
|
+
>
|
|
163
|
+
<input
|
|
164
|
+
v-model="newAddress.email"
|
|
165
|
+
type="email"
|
|
166
|
+
placeholder="support@example.com"
|
|
167
|
+
class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
<div>
|
|
171
|
+
<label class="block text-xs font-medium text-[var(--esc-panel-text-tertiary)]"
|
|
172
|
+
>Display Name</label
|
|
173
|
+
>
|
|
174
|
+
<input
|
|
175
|
+
v-model="newAddress.display_name"
|
|
176
|
+
type="text"
|
|
177
|
+
placeholder="Support Team"
|
|
178
|
+
class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
179
|
+
/>
|
|
180
|
+
</div>
|
|
181
|
+
<div>
|
|
182
|
+
<label class="block text-xs font-medium text-[var(--esc-panel-text-tertiary)]"
|
|
183
|
+
>Department Routing</label
|
|
184
|
+
>
|
|
185
|
+
<input
|
|
186
|
+
v-model.number="newAddress.department_id"
|
|
187
|
+
type="number"
|
|
188
|
+
placeholder="All departments"
|
|
189
|
+
class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="mt-3 flex gap-2">
|
|
194
|
+
<button
|
|
195
|
+
type="button"
|
|
196
|
+
class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-1.5 text-sm font-medium text-white"
|
|
197
|
+
:disabled="!newAddress.email"
|
|
198
|
+
@click="addAddress"
|
|
199
|
+
>
|
|
200
|
+
Add
|
|
201
|
+
</button>
|
|
202
|
+
<button
|
|
203
|
+
type="button"
|
|
204
|
+
class="rounded-lg border border-[var(--esc-panel-border-input)] px-4 py-1.5 text-sm text-[var(--esc-panel-text-tertiary)] hover:text-[var(--esc-panel-text-secondary)]"
|
|
205
|
+
@click="showAddForm = false"
|
|
206
|
+
>
|
|
207
|
+
Cancel
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<!-- Default Reply Address -->
|
|
214
|
+
<div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
|
|
215
|
+
<h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Default Reply Address</h3>
|
|
216
|
+
<p class="mb-3 text-xs text-[var(--esc-panel-text-muted)]">
|
|
217
|
+
Select the default email address for outbound replies
|
|
218
|
+
</p>
|
|
219
|
+
<select
|
|
220
|
+
v-model="form.default_reply_address"
|
|
221
|
+
class="w-full max-w-sm rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
|
|
222
|
+
>
|
|
223
|
+
<option value="">Select an address...</option>
|
|
224
|
+
<option v-for="(addr, idx) in form.addresses" :key="idx" :value="addr.email">
|
|
225
|
+
{{ addr.display_name ? `${addr.display_name} <${addr.email}>` : addr.email }}
|
|
226
|
+
</option>
|
|
227
|
+
</select>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<!-- Submit -->
|
|
231
|
+
<div class="flex items-center justify-end gap-3">
|
|
232
|
+
<span v-if="form.recentlySuccessful" class="text-sm text-emerald-400">Saved.</span>
|
|
233
|
+
<button
|
|
234
|
+
type="submit"
|
|
235
|
+
:disabled="form.processing"
|
|
236
|
+
class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-5 py-2 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)] disabled:opacity-50"
|
|
237
|
+
>
|
|
238
|
+
{{ form.processing ? 'Saving...' : 'Save Email Settings' }}
|
|
239
|
+
</button>
|
|
240
|
+
</div>
|
|
241
|
+
</form>
|
|
242
|
+
</EscalatedLayout>
|
|
243
|
+
</template>
|