@escalated-dev/escalated 0.8.0 → 0.11.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.
Files changed (124) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +388 -361
  3. package/package.json +85 -84
  4. package/src/components/ActiveChatsPanel.vue +246 -246
  5. package/src/components/AdminDashboard.stories.js +1423 -1423
  6. package/src/components/AttachmentList.vue +65 -65
  7. package/src/components/ChatActionBar.vue +226 -226
  8. package/src/components/ChatBubble.vue +162 -162
  9. package/src/components/ChatComposer.vue +308 -308
  10. package/src/components/ChatQueue.vue +222 -222
  11. package/src/components/ChatThread.vue +197 -197
  12. package/src/components/CustomTicketActions.stories.js +71 -0
  13. package/src/components/CustomTicketActions.vue +101 -0
  14. package/src/components/EscalatedLayout.vue +628 -604
  15. package/src/components/FollowButton.vue +20 -1
  16. package/src/components/MentionDropdown.vue +154 -154
  17. package/src/components/MetricComparison.vue +89 -89
  18. package/src/components/PercentileChart.vue +67 -67
  19. package/src/components/PeriodSelector.vue +98 -98
  20. package/src/components/PresenceIndicator.vue +228 -213
  21. package/src/components/ReplyComposer.vue +336 -336
  22. package/src/components/ReplyThread.vue +153 -153
  23. package/src/components/ReportChart.vue +395 -395
  24. package/src/components/ReportExportButton.vue +108 -108
  25. package/src/components/SaveViewDialog.vue +172 -172
  26. package/src/components/SavedViewSidebar.vue +208 -208
  27. package/src/components/SkillTagManager.vue +3 -0
  28. package/src/components/SnoozeButton.vue +247 -247
  29. package/src/components/TicketFilters.vue +270 -270
  30. package/src/components/TicketList.vue +655 -655
  31. package/src/components/TicketSidebar.vue +4 -0
  32. package/src/components/TicketSplitDialog.vue +98 -98
  33. package/src/components/TicketSubjects.stories.js +63 -0
  34. package/src/components/TicketSubjects.vue +107 -0
  35. package/src/components/TwoFactorSetup.vue +1 -37
  36. package/src/components/VariableTokenInput.vue +111 -111
  37. package/src/components/WorkflowActionConfig.vue +287 -287
  38. package/src/components/WorkflowActionList.vue +350 -350
  39. package/src/components/WorkflowConditionBuilder.vue +265 -265
  40. package/src/components/WorkflowTriggerSelector.vue +125 -125
  41. package/src/components/admin/newsletters/AnalyticsTiles.stories.js +14 -0
  42. package/src/components/admin/newsletters/AnalyticsTiles.vue +55 -0
  43. package/src/components/admin/newsletters/DeliveriesTable.stories.js +28 -0
  44. package/src/components/admin/newsletters/DeliveriesTable.vue +104 -0
  45. package/src/components/admin/newsletters/DynamicFilterBuilder.stories.js +11 -0
  46. package/src/components/admin/newsletters/DynamicFilterBuilder.vue +53 -0
  47. package/src/components/admin/newsletters/ListMemberTable.stories.js +12 -0
  48. package/src/components/admin/newsletters/ListMemberTable.vue +53 -0
  49. package/src/components/admin/newsletters/MarkdownEditor.stories.js +6 -0
  50. package/src/components/admin/newsletters/MarkdownEditor.vue +53 -0
  51. package/src/components/admin/newsletters/MergeFieldDropdown.stories.js +6 -0
  52. package/src/components/admin/newsletters/MergeFieldDropdown.vue +84 -0
  53. package/src/components/admin/newsletters/PreviewIframe.stories.js +7 -0
  54. package/src/components/admin/newsletters/PreviewIframe.vue +40 -0
  55. package/src/components/admin/newsletters/SummaryTiles.stories.js +8 -0
  56. package/src/components/admin/newsletters/SummaryTiles.vue +63 -0
  57. package/src/composables/useChat.js +187 -187
  58. package/src/composables/useI18n.js +102 -102
  59. package/src/composables/useMentions.js +134 -134
  60. package/src/composables/usePermissions.js +31 -0
  61. package/src/composables/useRealtime.js +182 -182
  62. package/src/index.js +85 -84
  63. package/src/locales/README.md +13 -13
  64. package/src/locales/ar.json +727 -727
  65. package/src/locales/de.json +690 -690
  66. package/src/locales/en.json +889 -729
  67. package/src/locales/es.json +691 -691
  68. package/src/locales/fr.json +691 -691
  69. package/src/locales/index.js +16 -16
  70. package/src/locales/it.json +727 -727
  71. package/src/locales/ja.json +727 -727
  72. package/src/locales/ko.json +727 -727
  73. package/src/locales/nl.json +727 -727
  74. package/src/locales/pl.json +727 -727
  75. package/src/locales/pt-BR.json +727 -727
  76. package/src/locales/ru.json +727 -727
  77. package/src/locales/tr.json +727 -727
  78. package/src/locales/zh-CN.json +727 -727
  79. package/src/pages/Admin/Automations/Form.vue +280 -0
  80. package/src/pages/Admin/Automations/Index.vue +230 -0
  81. package/src/pages/Admin/Newsletters/Compose.vue +192 -0
  82. package/src/pages/Admin/Newsletters/Edit.vue +24 -0
  83. package/src/pages/Admin/Newsletters/Index.vue +141 -0
  84. package/src/pages/Admin/Newsletters/Lists/Create.vue +64 -0
  85. package/src/pages/Admin/Newsletters/Lists/Index.vue +59 -0
  86. package/src/pages/Admin/Newsletters/Lists/Show.vue +53 -0
  87. package/src/pages/Admin/Newsletters/Settings.vue +85 -0
  88. package/src/pages/Admin/Newsletters/Show.vue +140 -0
  89. package/src/pages/Admin/Newsletters/Templates/Create.vue +11 -0
  90. package/src/pages/Admin/Newsletters/Templates/Index.vue +54 -0
  91. package/src/pages/Admin/Newsletters/Templates/Show.vue +65 -0
  92. package/src/pages/Admin/Newsletters/usePreview.js +44 -0
  93. package/src/pages/Admin/Plugins/Index.vue +10 -2
  94. package/src/pages/Admin/Reports/AgentDetail.vue +132 -132
  95. package/src/pages/Admin/Reports/AgentRanking.vue +246 -246
  96. package/src/pages/Admin/Reports/Cohorts.vue +180 -180
  97. package/src/pages/Admin/Reports/Comparison.vue +141 -141
  98. package/src/pages/Admin/Reports/Dashboard.vue +226 -226
  99. package/src/pages/Admin/Reports/ResolutionTimes.vue +164 -164
  100. package/src/pages/Admin/Reports/ResponseTimes.vue +164 -164
  101. package/src/pages/Admin/Reports/SlaTrends.vue +183 -183
  102. package/src/pages/Admin/Settings/DatabaseConnection.vue +228 -0
  103. package/src/pages/Admin/Settings/PublicTickets.vue +163 -0
  104. package/src/pages/Admin/Settings/SsoSettings.vue +157 -4
  105. package/src/pages/Admin/Settings/TwoFactor.vue +32 -6
  106. package/src/pages/Admin/Settings.vue +1008 -1008
  107. package/src/pages/Admin/Skills/Form.vue +30 -10
  108. package/src/pages/Admin/Tickets/Index.vue +126 -126
  109. package/src/pages/Admin/Tickets/Show.vue +254 -249
  110. package/src/pages/Admin/Users/Index.vue +145 -145
  111. package/src/pages/Admin/Webhooks/DeliveryLog.vue +1 -2
  112. package/src/pages/Admin/Workflows/Builder.vue +207 -207
  113. package/src/pages/Admin/Workflows/Form.vue +24 -0
  114. package/src/pages/Admin/Workflows/Index.vue +235 -235
  115. package/src/pages/Admin/Workflows/Logs.vue +286 -286
  116. package/src/pages/Agent/TicketIndex.vue +112 -112
  117. package/src/pages/Agent/TicketShow.vue +547 -494
  118. package/src/pages/Customer/Index.vue +56 -56
  119. package/src/pages/Customer/KnowledgeBase/Article.vue +129 -129
  120. package/src/pages/Plugin/Page.vue +61 -0
  121. package/src/plugin.js +175 -175
  122. package/src/utils/mentionHighlight.js +24 -24
  123. package/src/widget/EscalatedWidget.vue +804 -804
  124. package/src/widget/index.js +254 -254
@@ -1,1008 +1,1008 @@
1
- <script setup>
2
- import EscalatedLayout from '../../components/EscalatedLayout.vue';
3
- import PluginSlot from '../../components/PluginSlot.vue';
4
- import { useForm, usePage } from '@inertiajs/vue3';
5
- import { computed } from 'vue';
6
- import { usePluginExtensions } from '../../composables/usePluginExtensions';
7
-
8
- const props = defineProps({ settings: Object, departments: Array });
9
- const page = usePage();
10
-
11
- const form = useForm({
12
- guest_tickets_enabled: props.settings.guest_tickets_enabled,
13
- allow_customer_close: props.settings.allow_customer_close,
14
- auto_close_resolved_after_days: props.settings.auto_close_resolved_after_days,
15
- max_attachments_per_reply: props.settings.max_attachments_per_reply,
16
- max_attachment_size_kb: props.settings.max_attachment_size_kb,
17
- ticket_reference_prefix: props.settings.ticket_reference_prefix,
18
- inbound_email_enabled: props.settings.inbound_email_enabled ?? false,
19
- inbound_email_adapter: props.settings.inbound_email_adapter ?? 'mailgun',
20
- inbound_email_address: props.settings.inbound_email_address ?? '',
21
- mailgun_signing_key: props.settings.mailgun_signing_key ?? '',
22
- postmark_inbound_token: props.settings.postmark_inbound_token ?? '',
23
- ses_region: props.settings.ses_region ?? '',
24
- ses_topic_arn: props.settings.ses_topic_arn ?? '',
25
- imap_host: props.settings.imap_host ?? '',
26
- imap_port: props.settings.imap_port ?? 993,
27
- imap_encryption: props.settings.imap_encryption ?? 'ssl',
28
- imap_username: props.settings.imap_username ?? '',
29
- imap_password: props.settings.imap_password ?? '',
30
- imap_mailbox: props.settings.imap_mailbox ?? 'INBOX',
31
- show_powered_by: props.settings.show_powered_by ?? true,
32
- widget_enabled: props.settings.widget_enabled ?? false,
33
- widget_color: props.settings.widget_color ?? '#4F46E5',
34
- widget_position: props.settings.widget_position ?? 'bottom-right',
35
- widget_greeting: props.settings.widget_greeting ?? 'Hi there! How can we help?',
36
- widget_departments: props.settings.widget_departments ?? '',
37
- knowledge_base_enabled: props.settings.knowledge_base_enabled ?? true,
38
- knowledge_base_public: props.settings.knowledge_base_public ?? true,
39
- knowledge_base_feedback_enabled: props.settings.knowledge_base_feedback_enabled ?? true,
40
- // Chat settings
41
- chat_enabled: props.settings.chat_enabled ?? false,
42
- chat_sound_notifications: props.settings.chat_sound_notifications ?? true,
43
- chat_pre_chat_name: props.settings.chat_pre_chat_name ?? true,
44
- chat_pre_chat_email: props.settings.chat_pre_chat_email ?? true,
45
- chat_pre_chat_department: props.settings.chat_pre_chat_department ?? false,
46
- chat_routing_strategy: props.settings.chat_routing_strategy ?? 'round_robin',
47
- chat_offline_behavior: props.settings.chat_offline_behavior ?? 'ticket_form',
48
- chat_max_queue: props.settings.chat_max_queue ?? 10,
49
- chat_concurrent_limit: props.settings.chat_concurrent_limit ?? 5,
50
- chat_auto_close_minutes: props.settings.chat_auto_close_minutes ?? 30,
51
- chat_queue_message: props.settings.chat_queue_message ?? '',
52
- chat_offline_message: props.settings.chat_offline_message ?? '',
53
- // Widget theme
54
- widget_primary_color: props.settings.widget_primary_color ?? '#4F46E5',
55
- widget_background_color: props.settings.widget_background_color ?? '#ffffff',
56
- widget_text_color: props.settings.widget_text_color ?? '#1f2937',
57
- widget_agent_bubble_color: props.settings.widget_agent_bubble_color ?? '#4F46E5',
58
- widget_customer_bubble_color: props.settings.widget_customer_bubble_color ?? '#f3f4f6',
59
- widget_header_background: props.settings.widget_header_background ?? '#4F46E5',
60
- widget_font_family: props.settings.widget_font_family ?? 'system',
61
- widget_border_radius: props.settings.widget_border_radius ?? 12,
62
- widget_launcher_size: props.settings.widget_launcher_size ?? 'medium',
63
- widget_show_branding: props.settings.widget_show_branding ?? true,
64
- widget_custom_css: props.settings.widget_custom_css ?? '',
65
- widget_pre_chat_message: props.settings.widget_pre_chat_message ?? '',
66
- widget_chat_header_text: props.settings.widget_chat_header_text ?? 'Support',
67
- });
68
-
69
- const webhookBaseUrl = computed(() => {
70
- const prefix = page.props.escalated?.prefix || 'support';
71
- const origin = typeof window !== 'undefined' ? window.location.origin : '';
72
- return `${origin}/${prefix}/inbound`;
73
- });
74
-
75
- const { getPageComponents } = usePluginExtensions();
76
-
77
- function submit() {
78
- form.post(route('escalated.admin.settings.update'));
79
- }
80
- </script>
81
-
82
- <template>
83
- <EscalatedLayout title="Settings">
84
- <form class="mx-auto max-w-2xl space-y-6" @submit.prevent="submit">
85
- <!-- General -->
86
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
87
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">General</h3>
88
- <div class="space-y-5">
89
- <label class="flex items-center justify-between">
90
- <div>
91
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
92
- >Guest Tickets</span
93
- >
94
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
95
- Allow visitors to submit tickets without signing in
96
- </p>
97
- </div>
98
- <button
99
- type="button"
100
- :class="[
101
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
102
- form.guest_tickets_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
103
- ]"
104
- @click="form.guest_tickets_enabled = !form.guest_tickets_enabled"
105
- >
106
- <span
107
- :class="[
108
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
109
- form.guest_tickets_enabled ? 'translate-x-5' : 'translate-x-0',
110
- ]"
111
- />
112
- </button>
113
- </label>
114
-
115
- <label class="flex items-center justify-between">
116
- <div>
117
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
118
- >Allow Customer Close</span
119
- >
120
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
121
- Let customers close their own tickets
122
- </p>
123
- </div>
124
- <button
125
- type="button"
126
- :class="[
127
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
128
- form.allow_customer_close ? 'bg-emerald-500' : 'bg-neutral-700',
129
- ]"
130
- @click="form.allow_customer_close = !form.allow_customer_close"
131
- >
132
- <span
133
- :class="[
134
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
135
- form.allow_customer_close ? 'translate-x-5' : 'translate-x-0',
136
- ]"
137
- />
138
- </button>
139
- </label>
140
-
141
- <div>
142
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
143
- >Auto-close resolved tickets after</label
144
- >
145
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
146
- Days after resolution before auto-closing (0 to disable)
147
- </p>
148
- <div class="mt-2 flex items-center gap-2">
149
- <input
150
- v-model.number="form.auto_close_resolved_after_days"
151
- type="number"
152
- min="0"
153
- max="365"
154
- class="w-24 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)]"
155
- />
156
- <span class="text-sm text-[var(--esc-panel-text-muted)]">days</span>
157
- </div>
158
- </div>
159
- </div>
160
- </div>
161
-
162
- <!-- Limits -->
163
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
164
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Limits</h3>
165
- <div class="space-y-5">
166
- <div>
167
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
168
- >Max attachments per reply</label
169
- >
170
- <input
171
- v-model.number="form.max_attachments_per_reply"
172
- type="number"
173
- min="1"
174
- max="20"
175
- class="mt-2 w-24 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)]"
176
- />
177
- </div>
178
- <div>
179
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
180
- >Max attachment size</label
181
- >
182
- <div class="mt-2 flex items-center gap-2">
183
- <input
184
- v-model.number="form.max_attachment_size_kb"
185
- type="number"
186
- min="512"
187
- max="102400"
188
- step="512"
189
- class="w-32 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)]"
190
- />
191
- <span class="text-sm text-[var(--esc-panel-text-muted)]"
192
- >KB ({{ Math.round(form.max_attachment_size_kb / 1024) }} MB)</span
193
- >
194
- </div>
195
- </div>
196
- </div>
197
- </div>
198
-
199
- <!-- Tickets -->
200
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
201
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Tickets</h3>
202
- <div class="space-y-5">
203
- <div>
204
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
205
- >Reference Prefix</label
206
- >
207
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
208
- Prefix for ticket references (e.g. ESC produces ESC-00001)
209
- </p>
210
- <input
211
- v-model="form.ticket_reference_prefix"
212
- type="text"
213
- maxlength="10"
214
- class="mt-2 w-32 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)] uppercase focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
215
- />
216
- </div>
217
- </div>
218
- </div>
219
-
220
- <!-- Inbound Email -->
221
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
222
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Inbound Email</h3>
223
- <div class="space-y-5">
224
- <label class="flex items-center justify-between">
225
- <div>
226
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
227
- >Inbound Email</span
228
- >
229
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
230
- Allow creating and replying to tickets via email
231
- </p>
232
- </div>
233
- <button
234
- type="button"
235
- :class="[
236
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
237
- form.inbound_email_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
238
- ]"
239
- @click="form.inbound_email_enabled = !form.inbound_email_enabled"
240
- >
241
- <span
242
- :class="[
243
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
244
- form.inbound_email_enabled ? 'translate-x-5' : 'translate-x-0',
245
- ]"
246
- />
247
- </button>
248
- </label>
249
-
250
- <template v-if="form.inbound_email_enabled">
251
- <div>
252
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
253
- >Email Provider</label
254
- >
255
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
256
- How inbound emails are received
257
- </p>
258
- <select
259
- v-model="form.inbound_email_adapter"
260
- class="mt-2 w-48 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)]"
261
- >
262
- <option value="mailgun">Mailgun</option>
263
- <option value="postmark">Postmark</option>
264
- <option value="ses">AWS SES</option>
265
- <option value="imap">IMAP</option>
266
- </select>
267
- </div>
268
-
269
- <div>
270
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
271
- >Support Email Address</label
272
- >
273
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
274
- The email address customers will send emails to (e.g. support@example.com)
275
- </p>
276
- <input
277
- v-model="form.inbound_email_address"
278
- type="email"
279
- placeholder="support@example.com"
280
- class="mt-2 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)]"
281
- />
282
- </div>
283
-
284
- <!-- Mailgun -->
285
- <template v-if="form.inbound_email_adapter === 'mailgun'">
286
- <div>
287
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
288
- >Signing Key</label
289
- >
290
- <input
291
- v-model="form.mailgun_signing_key"
292
- type="password"
293
- class="mt-2 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)]"
294
- />
295
- </div>
296
- <div>
297
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
298
- >Webhook URL</label
299
- >
300
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
301
- Configure this URL in your Mailgun dashboard
302
- </p>
303
- <input
304
- :value="`${webhookBaseUrl}/mailgun`"
305
- type="text"
306
- readonly
307
- class="mt-2 w-full max-w-lg 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-tertiary)] select-all cursor-default focus:outline-none"
308
- />
309
- </div>
310
- </template>
311
-
312
- <!-- Postmark -->
313
- <template v-if="form.inbound_email_adapter === 'postmark'">
314
- <div>
315
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
316
- >Inbound Token</label
317
- >
318
- <input
319
- v-model="form.postmark_inbound_token"
320
- type="password"
321
- class="mt-2 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)]"
322
- />
323
- </div>
324
- <div>
325
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
326
- >Webhook URL</label
327
- >
328
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
329
- Configure this URL in your Postmark server settings
330
- </p>
331
- <input
332
- :value="`${webhookBaseUrl}/postmark`"
333
- type="text"
334
- readonly
335
- class="mt-2 w-full max-w-lg 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-tertiary)] select-all cursor-default focus:outline-none"
336
- />
337
- </div>
338
- </template>
339
-
340
- <!-- AWS SES -->
341
- <template v-if="form.inbound_email_adapter === 'ses'">
342
- <div>
343
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
344
- >Region</label
345
- >
346
- <input
347
- v-model="form.ses_region"
348
- type="text"
349
- placeholder="us-east-1"
350
- class="mt-2 w-48 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)]"
351
- />
352
- </div>
353
- <div>
354
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
355
- >Topic ARN</label
356
- >
357
- <input
358
- v-model="form.ses_topic_arn"
359
- type="text"
360
- placeholder="arn:aws:sns:us-east-1:123456789:ses-inbound"
361
- class="mt-2 w-full max-w-lg 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)]"
362
- />
363
- </div>
364
- <div>
365
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
366
- >Webhook URL</label
367
- >
368
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
369
- Configure this URL as your SNS subscription endpoint
370
- </p>
371
- <input
372
- :value="`${webhookBaseUrl}/ses`"
373
- type="text"
374
- readonly
375
- class="mt-2 w-full max-w-lg 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-tertiary)] select-all cursor-default focus:outline-none"
376
- />
377
- </div>
378
- </template>
379
-
380
- <!-- IMAP -->
381
- <template v-if="form.inbound_email_adapter === 'imap'">
382
- <div>
383
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
384
- >Host</label
385
- >
386
- <input
387
- v-model="form.imap_host"
388
- type="text"
389
- placeholder="imap.example.com"
390
- class="mt-2 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)]"
391
- />
392
- </div>
393
- <div class="flex items-end gap-4">
394
- <div>
395
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
396
- >Port</label
397
- >
398
- <input
399
- v-model.number="form.imap_port"
400
- type="number"
401
- min="1"
402
- max="65535"
403
- class="mt-2 w-24 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)]"
404
- />
405
- </div>
406
- <div>
407
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
408
- >Encryption</label
409
- >
410
- <select
411
- v-model="form.imap_encryption"
412
- class="mt-2 w-28 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)]"
413
- >
414
- <option value="ssl">SSL</option>
415
- <option value="tls">TLS</option>
416
- <option value="none">None</option>
417
- </select>
418
- </div>
419
- </div>
420
- <div>
421
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
422
- >Username</label
423
- >
424
- <input
425
- v-model="form.imap_username"
426
- type="text"
427
- class="mt-2 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)]"
428
- />
429
- </div>
430
- <div>
431
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
432
- >Password</label
433
- >
434
- <input
435
- v-model="form.imap_password"
436
- type="password"
437
- class="mt-2 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)]"
438
- />
439
- </div>
440
- <div>
441
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
442
- >Mailbox</label
443
- >
444
- <input
445
- v-model="form.imap_mailbox"
446
- type="text"
447
- placeholder="INBOX"
448
- class="mt-2 w-48 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)]"
449
- />
450
- </div>
451
- </template>
452
- </template>
453
- </div>
454
- </div>
455
-
456
- <!-- Knowledge Base -->
457
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
458
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Knowledge Base</h3>
459
- <div class="space-y-5">
460
- <label class="flex items-center justify-between">
461
- <div>
462
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
463
- >Enable Knowledge Base</span
464
- >
465
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
466
- Show the knowledge base to customers and visitors
467
- </p>
468
- </div>
469
- <button
470
- type="button"
471
- :class="[
472
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
473
- form.knowledge_base_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
474
- ]"
475
- @click="form.knowledge_base_enabled = !form.knowledge_base_enabled"
476
- >
477
- <span
478
- :class="[
479
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
480
- form.knowledge_base_enabled ? 'translate-x-5' : 'translate-x-0',
481
- ]"
482
- />
483
- </button>
484
- </label>
485
- <label class="flex items-center justify-between">
486
- <div>
487
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
488
- >Public Access</span
489
- >
490
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
491
- Allow unauthenticated visitors to browse the knowledge base
492
- </p>
493
- </div>
494
- <button
495
- type="button"
496
- :class="[
497
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
498
- form.knowledge_base_public ? 'bg-emerald-500' : 'bg-neutral-700',
499
- ]"
500
- @click="form.knowledge_base_public = !form.knowledge_base_public"
501
- >
502
- <span
503
- :class="[
504
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
505
- form.knowledge_base_public ? 'translate-x-5' : 'translate-x-0',
506
- ]"
507
- />
508
- </button>
509
- </label>
510
- <label class="flex items-center justify-between">
511
- <div>
512
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
513
- >Article Feedback</span
514
- >
515
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
516
- Show helpful / not helpful buttons on articles
517
- </p>
518
- </div>
519
- <button
520
- type="button"
521
- :class="[
522
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
523
- form.knowledge_base_feedback_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
524
- ]"
525
- @click="form.knowledge_base_feedback_enabled = !form.knowledge_base_feedback_enabled"
526
- >
527
- <span
528
- :class="[
529
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
530
- form.knowledge_base_feedback_enabled ? 'translate-x-5' : 'translate-x-0',
531
- ]"
532
- />
533
- </button>
534
- </label>
535
- </div>
536
- </div>
537
-
538
- <!-- Branding -->
539
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
540
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Branding</h3>
541
- <div class="space-y-5">
542
- <label class="flex items-center justify-between">
543
- <div>
544
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
545
- >Show "Powered by Escalated"</span
546
- >
547
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
548
- Display attribution badge in the support portal footer
549
- </p>
550
- </div>
551
- <button
552
- type="button"
553
- :class="[
554
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
555
- form.show_powered_by ? 'bg-emerald-500' : 'bg-neutral-700',
556
- ]"
557
- @click="form.show_powered_by = !form.show_powered_by"
558
- >
559
- <span
560
- :class="[
561
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
562
- form.show_powered_by ? 'translate-x-5' : 'translate-x-0',
563
- ]"
564
- />
565
- </button>
566
- </label>
567
- </div>
568
- </div>
569
-
570
- <!-- Support Widget -->
571
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
572
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Support Widget</h3>
573
- <div class="space-y-5">
574
- <label class="flex items-center justify-between">
575
- <div>
576
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
577
- >Enable Widget</span
578
- >
579
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
580
- Show an embeddable support widget on your site
581
- </p>
582
- </div>
583
- <button
584
- type="button"
585
- :class="[
586
- 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
587
- form.widget_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
588
- ]"
589
- @click="form.widget_enabled = !form.widget_enabled"
590
- >
591
- <span
592
- :class="[
593
- 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
594
- form.widget_enabled ? 'translate-x-5' : 'translate-x-0',
595
- ]"
596
- />
597
- </button>
598
- </label>
599
- <template v-if="form.widget_enabled">
600
- <div>
601
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
602
- >Widget Color</label
603
- >
604
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
605
- Primary color for the widget button and header
606
- </p>
607
- <div class="mt-2 flex items-center gap-3">
608
- <input
609
- v-model="form.widget_color"
610
- type="color"
611
- class="h-9 w-12 cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] p-0.5"
612
- />
613
- <input
614
- v-model="form.widget_color"
615
- type="text"
616
- maxlength="7"
617
- class="w-28 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)] uppercase focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
618
- />
619
- </div>
620
- </div>
621
- <div>
622
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
623
- >Position</label
624
- >
625
- <select
626
- v-model="form.widget_position"
627
- class="mt-2 w-48 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)]"
628
- >
629
- <option value="bottom-right">Bottom Right</option>
630
- <option value="bottom-left">Bottom Left</option>
631
- </select>
632
- </div>
633
- <div>
634
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
635
- >Greeting Message</label
636
- >
637
- <input
638
- v-model="form.widget_greeting"
639
- type="text"
640
- maxlength="255"
641
- class="mt-2 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)]"
642
- />
643
- </div>
644
- <div v-if="departments?.length">
645
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
646
- >Departments</label
647
- >
648
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
649
- Select which departments appear in the widget (leave empty for all)
650
- </p>
651
- <div class="mt-2 space-y-2">
652
- <label v-for="dept in departments" :key="dept.id" class="flex items-center gap-2">
653
- <input
654
- type="checkbox"
655
- :value="dept.id"
656
- :checked="
657
- form.widget_departments.split(',').filter(Boolean).includes(String(dept.id))
658
- "
659
- class="rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-emerald-500 focus:ring-emerald-500"
660
- @change="
661
- (e) => {
662
- const ids = form.widget_departments.split(',').filter(Boolean);
663
- if (e.target.checked) {
664
- ids.push(String(dept.id));
665
- } else {
666
- const idx = ids.indexOf(String(dept.id));
667
- if (idx > -1) ids.splice(idx, 1);
668
- }
669
- form.widget_departments = ids.join(',');
670
- }
671
- "
672
- />
673
- <span class="text-sm text-[var(--esc-panel-text-secondary)]">{{ dept.name }}</span>
674
- </label>
675
- </div>
676
- </div>
677
- <div
678
- class="rounded-lg border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface-alt)] p-4"
679
- >
680
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
681
- >Embed Code</label
682
- >
683
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
684
- Add this snippet to your website to show the support widget
685
- </p>
686
- <code
687
- class="mt-2 block whitespace-pre-wrap break-all rounded-lg bg-[var(--esc-panel-bg)] p-3 text-xs text-[var(--esc-panel-text-tertiary)] select-all"
688
- >&lt;script src="/{{ page.props.escalated?.prefix || 'support' }}/widget/embed.js"
689
- data-color="{{ form.widget_color }}" data-position="{{
690
- form.widget_position
691
- }}"&gt;&lt;/script&gt;</code
692
- >
693
- </div>
694
- </template>
695
- </div>
696
- </div>
697
-
698
- <!-- Live Chat -->
699
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
700
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Live Chat</h3>
701
- <div class="space-y-5">
702
- <label class="flex items-center justify-between">
703
- <div>
704
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
705
- >Enable Live Chat</span
706
- >
707
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
708
- Allow customers to start real-time chat sessions
709
- </p>
710
- </div>
711
- <input
712
- v-model="form.chat_enabled"
713
- type="checkbox"
714
- :true-value="true"
715
- :false-value="false"
716
- class="h-4 w-4 rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
717
- />
718
- </label>
719
- <label class="flex items-center justify-between">
720
- <div>
721
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
722
- >Sound Notifications</span
723
- >
724
- <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
725
- Play sound on new chat messages
726
- </p>
727
- </div>
728
- <input
729
- v-model="form.chat_sound_notifications"
730
- type="checkbox"
731
- :true-value="true"
732
- :false-value="false"
733
- class="h-4 w-4 rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
734
- />
735
- </label>
736
- </div>
737
- </div>
738
-
739
- <!-- Chat Routing -->
740
- <div
741
- v-if="form.chat_enabled"
742
- class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6"
743
- >
744
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Chat Routing</h3>
745
- <div class="space-y-5">
746
- <div>
747
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
748
- >Routing Strategy</label
749
- >
750
- <select
751
- v-model="form.chat_routing_strategy"
752
- class="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)]"
753
- >
754
- <option value="round_robin">Round Robin</option>
755
- <option value="least_active">Least Active</option>
756
- <option value="manual">Manual (Queue)</option>
757
- </select>
758
- </div>
759
- <div>
760
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
761
- >Offline Behavior</label
762
- >
763
- <select
764
- v-model="form.chat_offline_behavior"
765
- class="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)]"
766
- >
767
- <option value="ticket_form">Show Ticket Form</option>
768
- <option value="offline_message">Show Offline Message</option>
769
- <option value="hide">Hide Chat</option>
770
- </select>
771
- </div>
772
- <div class="grid grid-cols-2 gap-4">
773
- <div>
774
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
775
- >Max Queue Size</label
776
- >
777
- <input
778
- v-model.number="form.chat_max_queue"
779
- type="number"
780
- min="1"
781
- class="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)]"
782
- />
783
- </div>
784
- <div>
785
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
786
- >Concurrent Limit</label
787
- >
788
- <input
789
- v-model.number="form.chat_concurrent_limit"
790
- type="number"
791
- min="1"
792
- class="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)]"
793
- />
794
- </div>
795
- </div>
796
- <div>
797
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
798
- >Auto-close after (minutes)</label
799
- >
800
- <input
801
- v-model.number="form.chat_auto_close_minutes"
802
- type="number"
803
- min="1"
804
- class="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)]"
805
- />
806
- </div>
807
- <div>
808
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
809
- >Queue Message</label
810
- >
811
- <input
812
- v-model="form.chat_queue_message"
813
- type="text"
814
- placeholder="You're in the queue..."
815
- class="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)]"
816
- />
817
- </div>
818
- <div>
819
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
820
- >Offline Message</label
821
- >
822
- <input
823
- v-model="form.chat_offline_message"
824
- type="text"
825
- placeholder="Sorry, we're not available right now..."
826
- class="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)]"
827
- />
828
- </div>
829
- </div>
830
- </div>
831
-
832
- <!-- Widget Theme -->
833
- <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
834
- <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Widget Theme</h3>
835
- <div class="space-y-5">
836
- <div class="grid grid-cols-2 gap-4">
837
- <div>
838
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
839
- >Primary Color</label
840
- >
841
- <input
842
- v-model="form.widget_primary_color"
843
- type="color"
844
- class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
845
- />
846
- </div>
847
- <div>
848
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
849
- >Header Background</label
850
- >
851
- <input
852
- v-model="form.widget_header_background"
853
- type="color"
854
- class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
855
- />
856
- </div>
857
- <div>
858
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
859
- >Agent Bubble Color</label
860
- >
861
- <input
862
- v-model="form.widget_agent_bubble_color"
863
- type="color"
864
- class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
865
- />
866
- </div>
867
- <div>
868
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
869
- >Customer Bubble Color</label
870
- >
871
- <input
872
- v-model="form.widget_customer_bubble_color"
873
- type="color"
874
- class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
875
- />
876
- </div>
877
- <div>
878
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
879
- >Background Color</label
880
- >
881
- <input
882
- v-model="form.widget_background_color"
883
- type="color"
884
- class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
885
- />
886
- </div>
887
- <div>
888
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
889
- >Text Color</label
890
- >
891
- <input
892
- v-model="form.widget_text_color"
893
- type="color"
894
- class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
895
- />
896
- </div>
897
- </div>
898
- <div>
899
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
900
- >Font Family</label
901
- >
902
- <select
903
- v-model="form.widget_font_family"
904
- class="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)]"
905
- >
906
- <option value="system">System Default</option>
907
- <option value="Inter">Inter</option>
908
- <option value="Roboto">Roboto</option>
909
- <option value="Open Sans">Open Sans</option>
910
- <option value="Lato">Lato</option>
911
- <option value="Poppins">Poppins</option>
912
- <option value="Nunito">Nunito</option>
913
- </select>
914
- </div>
915
- <div>
916
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
917
- >Border Radius ({{ form.widget_border_radius }}px)</label
918
- >
919
- <input
920
- v-model.number="form.widget_border_radius"
921
- type="range"
922
- min="0"
923
- max="20"
924
- class="w-full"
925
- />
926
- </div>
927
- <div>
928
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
929
- >Launcher Size</label
930
- >
931
- <div class="flex gap-3">
932
- <label
933
- v-for="size in ['small', 'medium', 'large']"
934
- :key="size"
935
- class="flex items-center gap-2"
936
- >
937
- <input
938
- v-model="form.widget_launcher_size"
939
- type="radio"
940
- :value="size"
941
- class="text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
942
- />
943
- <span class="text-sm capitalize text-[var(--esc-panel-text-secondary)]">{{
944
- size
945
- }}</span>
946
- </label>
947
- </div>
948
- </div>
949
- <label class="flex items-center justify-between">
950
- <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]">Show Branding</span>
951
- <input
952
- v-model="form.widget_show_branding"
953
- type="checkbox"
954
- :true-value="true"
955
- :false-value="false"
956
- class="h-4 w-4 rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
957
- />
958
- </label>
959
- <div>
960
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
961
- >Pre-chat Message</label
962
- >
963
- <input
964
- v-model="form.widget_pre_chat_message"
965
- type="text"
966
- class="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)]"
967
- />
968
- </div>
969
- <div>
970
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
971
- >Chat Header Text</label
972
- >
973
- <input
974
- v-model="form.widget_chat_header_text"
975
- type="text"
976
- class="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)]"
977
- />
978
- </div>
979
- <div>
980
- <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
981
- >Custom CSS</label
982
- >
983
- <textarea
984
- v-model="form.widget_custom_css"
985
- rows="4"
986
- placeholder=".esc-w-panel { /* custom styles */ }"
987
- class="w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 font-mono text-xs text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)]"
988
- ></textarea>
989
- </div>
990
- </div>
991
- </div>
992
-
993
- <PluginSlot slot="admin.settings.extra" :components="getPageComponents('admin.settings', 'extra')" />
994
-
995
- <!-- Submit -->
996
- <div class="flex items-center justify-end gap-3">
997
- <span v-if="form.recentlySuccessful" class="text-sm text-emerald-400">Saved.</span>
998
- <button
999
- type="submit"
1000
- :disabled="form.processing"
1001
- 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"
1002
- >
1003
- {{ form.processing ? 'Saving...' : 'Save Settings' }}
1004
- </button>
1005
- </div>
1006
- </form>
1007
- </EscalatedLayout>
1008
- </template>
1
+ <script setup>
2
+ import EscalatedLayout from '../../components/EscalatedLayout.vue';
3
+ import PluginSlot from '../../components/PluginSlot.vue';
4
+ import { useForm, usePage } from '@inertiajs/vue3';
5
+ import { computed } from 'vue';
6
+ import { usePluginExtensions } from '../../composables/usePluginExtensions';
7
+
8
+ const props = defineProps({ settings: Object, departments: Array });
9
+ const page = usePage();
10
+
11
+ const form = useForm({
12
+ guest_tickets_enabled: props.settings.guest_tickets_enabled,
13
+ allow_customer_close: props.settings.allow_customer_close,
14
+ auto_close_resolved_after_days: props.settings.auto_close_resolved_after_days,
15
+ max_attachments_per_reply: props.settings.max_attachments_per_reply,
16
+ max_attachment_size_kb: props.settings.max_attachment_size_kb,
17
+ ticket_reference_prefix: props.settings.ticket_reference_prefix,
18
+ inbound_email_enabled: props.settings.inbound_email_enabled ?? false,
19
+ inbound_email_adapter: props.settings.inbound_email_adapter ?? 'mailgun',
20
+ inbound_email_address: props.settings.inbound_email_address ?? '',
21
+ mailgun_signing_key: props.settings.mailgun_signing_key ?? '',
22
+ postmark_inbound_token: props.settings.postmark_inbound_token ?? '',
23
+ ses_region: props.settings.ses_region ?? '',
24
+ ses_topic_arn: props.settings.ses_topic_arn ?? '',
25
+ imap_host: props.settings.imap_host ?? '',
26
+ imap_port: props.settings.imap_port ?? 993,
27
+ imap_encryption: props.settings.imap_encryption ?? 'ssl',
28
+ imap_username: props.settings.imap_username ?? '',
29
+ imap_password: props.settings.imap_password ?? '',
30
+ imap_mailbox: props.settings.imap_mailbox ?? 'INBOX',
31
+ show_powered_by: props.settings.show_powered_by ?? true,
32
+ widget_enabled: props.settings.widget_enabled ?? false,
33
+ widget_color: props.settings.widget_color ?? '#4F46E5',
34
+ widget_position: props.settings.widget_position ?? 'bottom-right',
35
+ widget_greeting: props.settings.widget_greeting ?? 'Hi there! How can we help?',
36
+ widget_departments: props.settings.widget_departments ?? '',
37
+ knowledge_base_enabled: props.settings.knowledge_base_enabled ?? true,
38
+ knowledge_base_public: props.settings.knowledge_base_public ?? true,
39
+ knowledge_base_feedback_enabled: props.settings.knowledge_base_feedback_enabled ?? true,
40
+ // Chat settings
41
+ chat_enabled: props.settings.chat_enabled ?? false,
42
+ chat_sound_notifications: props.settings.chat_sound_notifications ?? true,
43
+ chat_pre_chat_name: props.settings.chat_pre_chat_name ?? true,
44
+ chat_pre_chat_email: props.settings.chat_pre_chat_email ?? true,
45
+ chat_pre_chat_department: props.settings.chat_pre_chat_department ?? false,
46
+ chat_routing_strategy: props.settings.chat_routing_strategy ?? 'round_robin',
47
+ chat_offline_behavior: props.settings.chat_offline_behavior ?? 'ticket_form',
48
+ chat_max_queue: props.settings.chat_max_queue ?? 10,
49
+ chat_concurrent_limit: props.settings.chat_concurrent_limit ?? 5,
50
+ chat_auto_close_minutes: props.settings.chat_auto_close_minutes ?? 30,
51
+ chat_queue_message: props.settings.chat_queue_message ?? '',
52
+ chat_offline_message: props.settings.chat_offline_message ?? '',
53
+ // Widget theme
54
+ widget_primary_color: props.settings.widget_primary_color ?? '#4F46E5',
55
+ widget_background_color: props.settings.widget_background_color ?? '#ffffff',
56
+ widget_text_color: props.settings.widget_text_color ?? '#1f2937',
57
+ widget_agent_bubble_color: props.settings.widget_agent_bubble_color ?? '#4F46E5',
58
+ widget_customer_bubble_color: props.settings.widget_customer_bubble_color ?? '#f3f4f6',
59
+ widget_header_background: props.settings.widget_header_background ?? '#4F46E5',
60
+ widget_font_family: props.settings.widget_font_family ?? 'system',
61
+ widget_border_radius: props.settings.widget_border_radius ?? 12,
62
+ widget_launcher_size: props.settings.widget_launcher_size ?? 'medium',
63
+ widget_show_branding: props.settings.widget_show_branding ?? true,
64
+ widget_custom_css: props.settings.widget_custom_css ?? '',
65
+ widget_pre_chat_message: props.settings.widget_pre_chat_message ?? '',
66
+ widget_chat_header_text: props.settings.widget_chat_header_text ?? 'Support',
67
+ });
68
+
69
+ const webhookBaseUrl = computed(() => {
70
+ const prefix = page.props.escalated?.prefix || 'support';
71
+ const origin = typeof window !== 'undefined' ? window.location.origin : '';
72
+ return `${origin}/${prefix}/inbound`;
73
+ });
74
+
75
+ const { getPageComponents } = usePluginExtensions();
76
+
77
+ function submit() {
78
+ form.post(route('escalated.admin.settings.update'));
79
+ }
80
+ </script>
81
+
82
+ <template>
83
+ <EscalatedLayout title="Settings">
84
+ <form class="mx-auto max-w-2xl space-y-6" @submit.prevent="submit">
85
+ <!-- General -->
86
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
87
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">General</h3>
88
+ <div class="space-y-5">
89
+ <label class="flex items-center justify-between">
90
+ <div>
91
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
92
+ >Guest Tickets</span
93
+ >
94
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
95
+ Allow visitors to submit tickets without signing in
96
+ </p>
97
+ </div>
98
+ <button
99
+ type="button"
100
+ :class="[
101
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
102
+ form.guest_tickets_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
103
+ ]"
104
+ @click="form.guest_tickets_enabled = !form.guest_tickets_enabled"
105
+ >
106
+ <span
107
+ :class="[
108
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
109
+ form.guest_tickets_enabled ? 'translate-x-5' : 'translate-x-0',
110
+ ]"
111
+ />
112
+ </button>
113
+ </label>
114
+
115
+ <label class="flex items-center justify-between">
116
+ <div>
117
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
118
+ >Allow Customer Close</span
119
+ >
120
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
121
+ Let customers close their own tickets
122
+ </p>
123
+ </div>
124
+ <button
125
+ type="button"
126
+ :class="[
127
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
128
+ form.allow_customer_close ? 'bg-emerald-500' : 'bg-neutral-700',
129
+ ]"
130
+ @click="form.allow_customer_close = !form.allow_customer_close"
131
+ >
132
+ <span
133
+ :class="[
134
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
135
+ form.allow_customer_close ? 'translate-x-5' : 'translate-x-0',
136
+ ]"
137
+ />
138
+ </button>
139
+ </label>
140
+
141
+ <div>
142
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
143
+ >Auto-close resolved tickets after</label
144
+ >
145
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
146
+ Days after resolution before auto-closing (0 to disable)
147
+ </p>
148
+ <div class="mt-2 flex items-center gap-2">
149
+ <input
150
+ v-model.number="form.auto_close_resolved_after_days"
151
+ type="number"
152
+ min="0"
153
+ max="365"
154
+ class="w-24 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)]"
155
+ />
156
+ <span class="text-sm text-[var(--esc-panel-text-muted)]">days</span>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ <!-- Limits -->
163
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
164
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Limits</h3>
165
+ <div class="space-y-5">
166
+ <div>
167
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
168
+ >Max attachments per reply</label
169
+ >
170
+ <input
171
+ v-model.number="form.max_attachments_per_reply"
172
+ type="number"
173
+ min="1"
174
+ max="20"
175
+ class="mt-2 w-24 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)]"
176
+ />
177
+ </div>
178
+ <div>
179
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
180
+ >Max attachment size</label
181
+ >
182
+ <div class="mt-2 flex items-center gap-2">
183
+ <input
184
+ v-model.number="form.max_attachment_size_kb"
185
+ type="number"
186
+ min="512"
187
+ max="102400"
188
+ step="512"
189
+ class="w-32 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)]"
190
+ />
191
+ <span class="text-sm text-[var(--esc-panel-text-muted)]"
192
+ >KB ({{ Math.round(form.max_attachment_size_kb / 1024) }} MB)</span
193
+ >
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <!-- Tickets -->
200
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
201
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Tickets</h3>
202
+ <div class="space-y-5">
203
+ <div>
204
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
205
+ >Reference Prefix</label
206
+ >
207
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
208
+ Prefix for ticket references (e.g. ESC produces ESC-00001)
209
+ </p>
210
+ <input
211
+ v-model="form.ticket_reference_prefix"
212
+ type="text"
213
+ maxlength="10"
214
+ class="mt-2 w-32 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)] uppercase focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
215
+ />
216
+ </div>
217
+ </div>
218
+ </div>
219
+
220
+ <!-- Inbound Email -->
221
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
222
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Inbound Email</h3>
223
+ <div class="space-y-5">
224
+ <label class="flex items-center justify-between">
225
+ <div>
226
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
227
+ >Inbound Email</span
228
+ >
229
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
230
+ Allow creating and replying to tickets via email
231
+ </p>
232
+ </div>
233
+ <button
234
+ type="button"
235
+ :class="[
236
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
237
+ form.inbound_email_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
238
+ ]"
239
+ @click="form.inbound_email_enabled = !form.inbound_email_enabled"
240
+ >
241
+ <span
242
+ :class="[
243
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
244
+ form.inbound_email_enabled ? 'translate-x-5' : 'translate-x-0',
245
+ ]"
246
+ />
247
+ </button>
248
+ </label>
249
+
250
+ <template v-if="form.inbound_email_enabled">
251
+ <div>
252
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
253
+ >Email Provider</label
254
+ >
255
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
256
+ How inbound emails are received
257
+ </p>
258
+ <select
259
+ v-model="form.inbound_email_adapter"
260
+ class="mt-2 w-48 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)]"
261
+ >
262
+ <option value="mailgun">Mailgun</option>
263
+ <option value="postmark">Postmark</option>
264
+ <option value="ses">AWS SES</option>
265
+ <option value="imap">IMAP</option>
266
+ </select>
267
+ </div>
268
+
269
+ <div>
270
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
271
+ >Support Email Address</label
272
+ >
273
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
274
+ The email address customers will send emails to (e.g. support@example.com)
275
+ </p>
276
+ <input
277
+ v-model="form.inbound_email_address"
278
+ type="email"
279
+ placeholder="support@example.com"
280
+ class="mt-2 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)]"
281
+ />
282
+ </div>
283
+
284
+ <!-- Mailgun -->
285
+ <template v-if="form.inbound_email_adapter === 'mailgun'">
286
+ <div>
287
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
288
+ >Signing Key</label
289
+ >
290
+ <input
291
+ v-model="form.mailgun_signing_key"
292
+ type="password"
293
+ class="mt-2 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)]"
294
+ />
295
+ </div>
296
+ <div>
297
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
298
+ >Webhook URL</label
299
+ >
300
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
301
+ Configure this URL in your Mailgun dashboard
302
+ </p>
303
+ <input
304
+ :value="`${webhookBaseUrl}/mailgun`"
305
+ type="text"
306
+ readonly
307
+ class="mt-2 w-full max-w-lg 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-tertiary)] select-all cursor-default focus:outline-none"
308
+ />
309
+ </div>
310
+ </template>
311
+
312
+ <!-- Postmark -->
313
+ <template v-if="form.inbound_email_adapter === 'postmark'">
314
+ <div>
315
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
316
+ >Inbound Token</label
317
+ >
318
+ <input
319
+ v-model="form.postmark_inbound_token"
320
+ type="password"
321
+ class="mt-2 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)]"
322
+ />
323
+ </div>
324
+ <div>
325
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
326
+ >Webhook URL</label
327
+ >
328
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
329
+ Configure this URL in your Postmark server settings
330
+ </p>
331
+ <input
332
+ :value="`${webhookBaseUrl}/postmark`"
333
+ type="text"
334
+ readonly
335
+ class="mt-2 w-full max-w-lg 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-tertiary)] select-all cursor-default focus:outline-none"
336
+ />
337
+ </div>
338
+ </template>
339
+
340
+ <!-- AWS SES -->
341
+ <template v-if="form.inbound_email_adapter === 'ses'">
342
+ <div>
343
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
344
+ >Region</label
345
+ >
346
+ <input
347
+ v-model="form.ses_region"
348
+ type="text"
349
+ placeholder="us-east-1"
350
+ class="mt-2 w-48 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)]"
351
+ />
352
+ </div>
353
+ <div>
354
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
355
+ >Topic ARN</label
356
+ >
357
+ <input
358
+ v-model="form.ses_topic_arn"
359
+ type="text"
360
+ placeholder="arn:aws:sns:us-east-1:123456789:ses-inbound"
361
+ class="mt-2 w-full max-w-lg 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)]"
362
+ />
363
+ </div>
364
+ <div>
365
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
366
+ >Webhook URL</label
367
+ >
368
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
369
+ Configure this URL as your SNS subscription endpoint
370
+ </p>
371
+ <input
372
+ :value="`${webhookBaseUrl}/ses`"
373
+ type="text"
374
+ readonly
375
+ class="mt-2 w-full max-w-lg 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-tertiary)] select-all cursor-default focus:outline-none"
376
+ />
377
+ </div>
378
+ </template>
379
+
380
+ <!-- IMAP -->
381
+ <template v-if="form.inbound_email_adapter === 'imap'">
382
+ <div>
383
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
384
+ >Host</label
385
+ >
386
+ <input
387
+ v-model="form.imap_host"
388
+ type="text"
389
+ placeholder="imap.example.com"
390
+ class="mt-2 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)]"
391
+ />
392
+ </div>
393
+ <div class="flex items-end gap-4">
394
+ <div>
395
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
396
+ >Port</label
397
+ >
398
+ <input
399
+ v-model.number="form.imap_port"
400
+ type="number"
401
+ min="1"
402
+ max="65535"
403
+ class="mt-2 w-24 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)]"
404
+ />
405
+ </div>
406
+ <div>
407
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
408
+ >Encryption</label
409
+ >
410
+ <select
411
+ v-model="form.imap_encryption"
412
+ class="mt-2 w-28 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)]"
413
+ >
414
+ <option value="ssl">SSL</option>
415
+ <option value="tls">TLS</option>
416
+ <option value="none">None</option>
417
+ </select>
418
+ </div>
419
+ </div>
420
+ <div>
421
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
422
+ >Username</label
423
+ >
424
+ <input
425
+ v-model="form.imap_username"
426
+ type="text"
427
+ class="mt-2 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)]"
428
+ />
429
+ </div>
430
+ <div>
431
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
432
+ >Password</label
433
+ >
434
+ <input
435
+ v-model="form.imap_password"
436
+ type="password"
437
+ class="mt-2 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)]"
438
+ />
439
+ </div>
440
+ <div>
441
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
442
+ >Mailbox</label
443
+ >
444
+ <input
445
+ v-model="form.imap_mailbox"
446
+ type="text"
447
+ placeholder="INBOX"
448
+ class="mt-2 w-48 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)]"
449
+ />
450
+ </div>
451
+ </template>
452
+ </template>
453
+ </div>
454
+ </div>
455
+
456
+ <!-- Knowledge Base -->
457
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
458
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Knowledge Base</h3>
459
+ <div class="space-y-5">
460
+ <label class="flex items-center justify-between">
461
+ <div>
462
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
463
+ >Enable Knowledge Base</span
464
+ >
465
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
466
+ Show the knowledge base to customers and visitors
467
+ </p>
468
+ </div>
469
+ <button
470
+ type="button"
471
+ :class="[
472
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
473
+ form.knowledge_base_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
474
+ ]"
475
+ @click="form.knowledge_base_enabled = !form.knowledge_base_enabled"
476
+ >
477
+ <span
478
+ :class="[
479
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
480
+ form.knowledge_base_enabled ? 'translate-x-5' : 'translate-x-0',
481
+ ]"
482
+ />
483
+ </button>
484
+ </label>
485
+ <label class="flex items-center justify-between">
486
+ <div>
487
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
488
+ >Public Access</span
489
+ >
490
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
491
+ Allow unauthenticated visitors to browse the knowledge base
492
+ </p>
493
+ </div>
494
+ <button
495
+ type="button"
496
+ :class="[
497
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
498
+ form.knowledge_base_public ? 'bg-emerald-500' : 'bg-neutral-700',
499
+ ]"
500
+ @click="form.knowledge_base_public = !form.knowledge_base_public"
501
+ >
502
+ <span
503
+ :class="[
504
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
505
+ form.knowledge_base_public ? 'translate-x-5' : 'translate-x-0',
506
+ ]"
507
+ />
508
+ </button>
509
+ </label>
510
+ <label class="flex items-center justify-between">
511
+ <div>
512
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
513
+ >Article Feedback</span
514
+ >
515
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
516
+ Show helpful / not helpful buttons on articles
517
+ </p>
518
+ </div>
519
+ <button
520
+ type="button"
521
+ :class="[
522
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
523
+ form.knowledge_base_feedback_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
524
+ ]"
525
+ @click="form.knowledge_base_feedback_enabled = !form.knowledge_base_feedback_enabled"
526
+ >
527
+ <span
528
+ :class="[
529
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
530
+ form.knowledge_base_feedback_enabled ? 'translate-x-5' : 'translate-x-0',
531
+ ]"
532
+ />
533
+ </button>
534
+ </label>
535
+ </div>
536
+ </div>
537
+
538
+ <!-- Branding -->
539
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
540
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Branding</h3>
541
+ <div class="space-y-5">
542
+ <label class="flex items-center justify-between">
543
+ <div>
544
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
545
+ >Show "Powered by Escalated"</span
546
+ >
547
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
548
+ Display attribution badge in the support portal footer
549
+ </p>
550
+ </div>
551
+ <button
552
+ type="button"
553
+ :class="[
554
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
555
+ form.show_powered_by ? 'bg-emerald-500' : 'bg-neutral-700',
556
+ ]"
557
+ @click="form.show_powered_by = !form.show_powered_by"
558
+ >
559
+ <span
560
+ :class="[
561
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
562
+ form.show_powered_by ? 'translate-x-5' : 'translate-x-0',
563
+ ]"
564
+ />
565
+ </button>
566
+ </label>
567
+ </div>
568
+ </div>
569
+
570
+ <!-- Support Widget -->
571
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
572
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Support Widget</h3>
573
+ <div class="space-y-5">
574
+ <label class="flex items-center justify-between">
575
+ <div>
576
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
577
+ >Enable Widget</span
578
+ >
579
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
580
+ Show an embeddable support widget on your site
581
+ </p>
582
+ </div>
583
+ <button
584
+ type="button"
585
+ :class="[
586
+ 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
587
+ form.widget_enabled ? 'bg-emerald-500' : 'bg-neutral-700',
588
+ ]"
589
+ @click="form.widget_enabled = !form.widget_enabled"
590
+ >
591
+ <span
592
+ :class="[
593
+ 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
594
+ form.widget_enabled ? 'translate-x-5' : 'translate-x-0',
595
+ ]"
596
+ />
597
+ </button>
598
+ </label>
599
+ <template v-if="form.widget_enabled">
600
+ <div>
601
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
602
+ >Widget Color</label
603
+ >
604
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
605
+ Primary color for the widget button and header
606
+ </p>
607
+ <div class="mt-2 flex items-center gap-3">
608
+ <input
609
+ v-model="form.widget_color"
610
+ type="color"
611
+ class="h-9 w-12 cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] p-0.5"
612
+ />
613
+ <input
614
+ v-model="form.widget_color"
615
+ type="text"
616
+ maxlength="7"
617
+ class="w-28 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)] uppercase focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
618
+ />
619
+ </div>
620
+ </div>
621
+ <div>
622
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
623
+ >Position</label
624
+ >
625
+ <select
626
+ v-model="form.widget_position"
627
+ class="mt-2 w-48 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)]"
628
+ >
629
+ <option value="bottom-right">Bottom Right</option>
630
+ <option value="bottom-left">Bottom Left</option>
631
+ </select>
632
+ </div>
633
+ <div>
634
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
635
+ >Greeting Message</label
636
+ >
637
+ <input
638
+ v-model="form.widget_greeting"
639
+ type="text"
640
+ maxlength="255"
641
+ class="mt-2 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)]"
642
+ />
643
+ </div>
644
+ <div v-if="departments?.length">
645
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
646
+ >Departments</label
647
+ >
648
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
649
+ Select which departments appear in the widget (leave empty for all)
650
+ </p>
651
+ <div class="mt-2 space-y-2">
652
+ <label v-for="dept in departments" :key="dept.id" class="flex items-center gap-2">
653
+ <input
654
+ type="checkbox"
655
+ :value="dept.id"
656
+ :checked="
657
+ form.widget_departments.split(',').filter(Boolean).includes(String(dept.id))
658
+ "
659
+ class="rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-emerald-500 focus:ring-emerald-500"
660
+ @change="
661
+ (e) => {
662
+ const ids = form.widget_departments.split(',').filter(Boolean);
663
+ if (e.target.checked) {
664
+ ids.push(String(dept.id));
665
+ } else {
666
+ const idx = ids.indexOf(String(dept.id));
667
+ if (idx > -1) ids.splice(idx, 1);
668
+ }
669
+ form.widget_departments = ids.join(',');
670
+ }
671
+ "
672
+ />
673
+ <span class="text-sm text-[var(--esc-panel-text-secondary)]">{{ dept.name }}</span>
674
+ </label>
675
+ </div>
676
+ </div>
677
+ <div
678
+ class="rounded-lg border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface-alt)] p-4"
679
+ >
680
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
681
+ >Embed Code</label
682
+ >
683
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
684
+ Add this snippet to your website to show the support widget
685
+ </p>
686
+ <code
687
+ class="mt-2 block whitespace-pre-wrap break-all rounded-lg bg-[var(--esc-panel-bg)] p-3 text-xs text-[var(--esc-panel-text-tertiary)] select-all"
688
+ >&lt;script src="/{{ page.props.escalated?.prefix || 'support' }}/widget/embed.js"
689
+ data-color="{{ form.widget_color }}" data-position="{{
690
+ form.widget_position
691
+ }}"&gt;&lt;/script&gt;</code
692
+ >
693
+ </div>
694
+ </template>
695
+ </div>
696
+ </div>
697
+
698
+ <!-- Live Chat -->
699
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
700
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Live Chat</h3>
701
+ <div class="space-y-5">
702
+ <label class="flex items-center justify-between">
703
+ <div>
704
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
705
+ >Enable Live Chat</span
706
+ >
707
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
708
+ Allow customers to start real-time chat sessions
709
+ </p>
710
+ </div>
711
+ <input
712
+ v-model="form.chat_enabled"
713
+ type="checkbox"
714
+ :true-value="true"
715
+ :false-value="false"
716
+ class="h-4 w-4 rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
717
+ />
718
+ </label>
719
+ <label class="flex items-center justify-between">
720
+ <div>
721
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]"
722
+ >Sound Notifications</span
723
+ >
724
+ <p class="mt-0.5 text-xs text-[var(--esc-panel-text-muted)]">
725
+ Play sound on new chat messages
726
+ </p>
727
+ </div>
728
+ <input
729
+ v-model="form.chat_sound_notifications"
730
+ type="checkbox"
731
+ :true-value="true"
732
+ :false-value="false"
733
+ class="h-4 w-4 rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
734
+ />
735
+ </label>
736
+ </div>
737
+ </div>
738
+
739
+ <!-- Chat Routing -->
740
+ <div
741
+ v-if="form.chat_enabled"
742
+ class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6"
743
+ >
744
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Chat Routing</h3>
745
+ <div class="space-y-5">
746
+ <div>
747
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
748
+ >Routing Strategy</label
749
+ >
750
+ <select
751
+ v-model="form.chat_routing_strategy"
752
+ class="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)]"
753
+ >
754
+ <option value="round_robin">Round Robin</option>
755
+ <option value="least_active">Least Active</option>
756
+ <option value="manual">Manual (Queue)</option>
757
+ </select>
758
+ </div>
759
+ <div>
760
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
761
+ >Offline Behavior</label
762
+ >
763
+ <select
764
+ v-model="form.chat_offline_behavior"
765
+ class="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)]"
766
+ >
767
+ <option value="ticket_form">Show Ticket Form</option>
768
+ <option value="offline_message">Show Offline Message</option>
769
+ <option value="hide">Hide Chat</option>
770
+ </select>
771
+ </div>
772
+ <div class="grid grid-cols-2 gap-4">
773
+ <div>
774
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
775
+ >Max Queue Size</label
776
+ >
777
+ <input
778
+ v-model.number="form.chat_max_queue"
779
+ type="number"
780
+ min="1"
781
+ class="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)]"
782
+ />
783
+ </div>
784
+ <div>
785
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
786
+ >Concurrent Limit</label
787
+ >
788
+ <input
789
+ v-model.number="form.chat_concurrent_limit"
790
+ type="number"
791
+ min="1"
792
+ class="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)]"
793
+ />
794
+ </div>
795
+ </div>
796
+ <div>
797
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
798
+ >Auto-close after (minutes)</label
799
+ >
800
+ <input
801
+ v-model.number="form.chat_auto_close_minutes"
802
+ type="number"
803
+ min="1"
804
+ class="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)]"
805
+ />
806
+ </div>
807
+ <div>
808
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
809
+ >Queue Message</label
810
+ >
811
+ <input
812
+ v-model="form.chat_queue_message"
813
+ type="text"
814
+ placeholder="You're in the queue..."
815
+ class="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)]"
816
+ />
817
+ </div>
818
+ <div>
819
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
820
+ >Offline Message</label
821
+ >
822
+ <input
823
+ v-model="form.chat_offline_message"
824
+ type="text"
825
+ placeholder="Sorry, we're not available right now..."
826
+ class="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)]"
827
+ />
828
+ </div>
829
+ </div>
830
+ </div>
831
+
832
+ <!-- Widget Theme -->
833
+ <div class="rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6">
834
+ <h3 class="mb-5 text-sm font-semibold text-[var(--esc-panel-text)]">Widget Theme</h3>
835
+ <div class="space-y-5">
836
+ <div class="grid grid-cols-2 gap-4">
837
+ <div>
838
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
839
+ >Primary Color</label
840
+ >
841
+ <input
842
+ v-model="form.widget_primary_color"
843
+ type="color"
844
+ class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
845
+ />
846
+ </div>
847
+ <div>
848
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
849
+ >Header Background</label
850
+ >
851
+ <input
852
+ v-model="form.widget_header_background"
853
+ type="color"
854
+ class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
855
+ />
856
+ </div>
857
+ <div>
858
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
859
+ >Agent Bubble Color</label
860
+ >
861
+ <input
862
+ v-model="form.widget_agent_bubble_color"
863
+ type="color"
864
+ class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
865
+ />
866
+ </div>
867
+ <div>
868
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
869
+ >Customer Bubble Color</label
870
+ >
871
+ <input
872
+ v-model="form.widget_customer_bubble_color"
873
+ type="color"
874
+ class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
875
+ />
876
+ </div>
877
+ <div>
878
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
879
+ >Background Color</label
880
+ >
881
+ <input
882
+ v-model="form.widget_background_color"
883
+ type="color"
884
+ class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
885
+ />
886
+ </div>
887
+ <div>
888
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
889
+ >Text Color</label
890
+ >
891
+ <input
892
+ v-model="form.widget_text_color"
893
+ type="color"
894
+ class="h-10 w-full cursor-pointer rounded-lg border border-[var(--esc-panel-border-input)]"
895
+ />
896
+ </div>
897
+ </div>
898
+ <div>
899
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
900
+ >Font Family</label
901
+ >
902
+ <select
903
+ v-model="form.widget_font_family"
904
+ class="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)]"
905
+ >
906
+ <option value="system">System Default</option>
907
+ <option value="Inter">Inter</option>
908
+ <option value="Roboto">Roboto</option>
909
+ <option value="Open Sans">Open Sans</option>
910
+ <option value="Lato">Lato</option>
911
+ <option value="Poppins">Poppins</option>
912
+ <option value="Nunito">Nunito</option>
913
+ </select>
914
+ </div>
915
+ <div>
916
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
917
+ >Border Radius ({{ form.widget_border_radius }}px)</label
918
+ >
919
+ <input
920
+ v-model.number="form.widget_border_radius"
921
+ type="range"
922
+ min="0"
923
+ max="20"
924
+ class="w-full"
925
+ />
926
+ </div>
927
+ <div>
928
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
929
+ >Launcher Size</label
930
+ >
931
+ <div class="flex gap-3">
932
+ <label
933
+ v-for="size in ['small', 'medium', 'large']"
934
+ :key="size"
935
+ class="flex items-center gap-2"
936
+ >
937
+ <input
938
+ v-model="form.widget_launcher_size"
939
+ type="radio"
940
+ :value="size"
941
+ class="text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
942
+ />
943
+ <span class="text-sm capitalize text-[var(--esc-panel-text-secondary)]">{{
944
+ size
945
+ }}</span>
946
+ </label>
947
+ </div>
948
+ </div>
949
+ <label class="flex items-center justify-between">
950
+ <span class="text-sm font-medium text-[var(--esc-panel-text-secondary)]">Show Branding</span>
951
+ <input
952
+ v-model="form.widget_show_branding"
953
+ type="checkbox"
954
+ :true-value="true"
955
+ :false-value="false"
956
+ class="h-4 w-4 rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] text-[var(--esc-panel-accent)] focus:ring-[var(--esc-panel-accent)]/20"
957
+ />
958
+ </label>
959
+ <div>
960
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
961
+ >Pre-chat Message</label
962
+ >
963
+ <input
964
+ v-model="form.widget_pre_chat_message"
965
+ type="text"
966
+ class="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)]"
967
+ />
968
+ </div>
969
+ <div>
970
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
971
+ >Chat Header Text</label
972
+ >
973
+ <input
974
+ v-model="form.widget_chat_header_text"
975
+ type="text"
976
+ class="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)]"
977
+ />
978
+ </div>
979
+ <div>
980
+ <label class="mb-1 block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
981
+ >Custom CSS</label
982
+ >
983
+ <textarea
984
+ v-model="form.widget_custom_css"
985
+ rows="4"
986
+ placeholder=".esc-w-panel { /* custom styles */ }"
987
+ class="w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] px-3 py-2 font-mono text-xs text-[var(--esc-panel-text-secondary)] placeholder-[var(--esc-panel-text-muted)]"
988
+ ></textarea>
989
+ </div>
990
+ </div>
991
+ </div>
992
+
993
+ <PluginSlot slot="admin.settings.extra" :components="getPageComponents('admin.settings', 'extra')" />
994
+
995
+ <!-- Submit -->
996
+ <div class="flex items-center justify-end gap-3">
997
+ <span v-if="form.recentlySuccessful" class="text-sm text-emerald-400">Saved.</span>
998
+ <button
999
+ type="submit"
1000
+ :disabled="form.processing"
1001
+ 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"
1002
+ >
1003
+ {{ form.processing ? 'Saving...' : 'Save Settings' }}
1004
+ </button>
1005
+ </div>
1006
+ </form>
1007
+ </EscalatedLayout>
1008
+ </template>