@escalated-dev/escalated 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +125 -2
  3. package/package.json +40 -5
  4. package/src/components/ActiveChatsPanel.vue +246 -0
  5. package/src/components/ActivityTimeline.stories.js +55 -0
  6. package/src/components/ActivityTimeline.vue +83 -66
  7. package/src/components/AdminDashboard.stories.js +1423 -0
  8. package/src/components/AgentLoadIndicator.stories.js +37 -0
  9. package/src/components/AgentLoadIndicator.vue +49 -0
  10. package/src/components/AgentPerformanceCard.vue +44 -0
  11. package/src/components/ArticleEditor.vue +82 -0
  12. package/src/components/AssigneeSelect.vue +37 -23
  13. package/src/components/AttachmentList.vue +65 -37
  14. package/src/components/AuditLogEntry.vue +95 -0
  15. package/src/components/BulkActionBar.vue +166 -97
  16. package/src/components/CategoryTree.vue +89 -0
  17. package/src/components/ChartWidget.vue +46 -0
  18. package/src/components/ChatActionBar.vue +226 -0
  19. package/src/components/ChatBubble.vue +162 -0
  20. package/src/components/ChatComposer.vue +308 -0
  21. package/src/components/ChatQueue.vue +222 -0
  22. package/src/components/ChatThread.vue +197 -0
  23. package/src/components/CollisionWarning.stories.js +30 -0
  24. package/src/components/CollisionWarning.vue +68 -0
  25. package/src/components/ConditionalFieldRules.vue +135 -0
  26. package/src/components/ContextPanel.vue +66 -0
  27. package/src/components/ContextPanelSection.vue +65 -0
  28. package/src/components/CustomFieldRenderer.vue +159 -0
  29. package/src/components/EscalatedLayout.vue +424 -70
  30. package/src/components/FileDropzone.vue +57 -41
  31. package/src/components/FollowButton.stories.js +30 -0
  32. package/src/components/FollowButton.vue +88 -63
  33. package/src/components/KeyboardShortcutHelp.vue +213 -123
  34. package/src/components/KnowledgePanel.vue +122 -0
  35. package/src/components/KpiCard.stories.js +32 -0
  36. package/src/components/KpiCard.vue +88 -0
  37. package/src/components/MacroDropdown.vue +150 -90
  38. package/src/components/MentionDropdown.vue +154 -0
  39. package/src/components/MetricComparison.vue +89 -0
  40. package/src/components/PercentileChart.vue +67 -0
  41. package/src/components/PeriodSelector.vue +98 -0
  42. package/src/components/PermissionMatrix.vue +107 -0
  43. package/src/components/PinnedNotes.vue +129 -90
  44. package/src/components/PluginSlot.vue +68 -62
  45. package/src/components/PresenceIndicator.vue +138 -28
  46. package/src/components/PriorityBadge.stories.js +33 -0
  47. package/src/components/PriorityBadge.vue +46 -39
  48. package/src/components/QuickFilters.vue +65 -49
  49. package/src/components/ReplyComposer.vue +207 -30
  50. package/src/components/ReplyThread.vue +111 -21
  51. package/src/components/ReportChart.vue +395 -0
  52. package/src/components/ReportExportButton.vue +108 -0
  53. package/src/components/SatisfactionRating.vue +180 -128
  54. package/src/components/SaveViewDialog.vue +172 -0
  55. package/src/components/SavedViewSidebar.vue +208 -0
  56. package/src/components/ScheduleEditor.vue +104 -0
  57. package/src/components/SideConversation.vue +165 -0
  58. package/src/components/SideConversationList.vue +147 -0
  59. package/src/components/SkillTagManager.vue +79 -0
  60. package/src/components/SlaComplianceChart.vue +46 -0
  61. package/src/components/SlaTimer.stories.js +35 -0
  62. package/src/components/SlaTimer.vue +62 -49
  63. package/src/components/SnoozeButton.vue +247 -0
  64. package/src/components/StatsCard.stories.js +24 -0
  65. package/src/components/StatsCard.vue +18 -6
  66. package/src/components/StatusBadge.stories.js +53 -0
  67. package/src/components/StatusBadge.vue +78 -46
  68. package/src/components/TagSelect.vue +84 -53
  69. package/src/components/TicketFilters.vue +247 -46
  70. package/src/components/TicketLinkPanel.vue +193 -0
  71. package/src/components/TicketList.vue +536 -74
  72. package/src/components/TicketMergeDialog.vue +157 -0
  73. package/src/components/TicketSidebar.vue +167 -99
  74. package/src/components/TicketSplitDialog.vue +98 -0
  75. package/src/components/TicketTypeSelector.vue +21 -0
  76. package/src/components/TotpInput.stories.js +9 -0
  77. package/src/components/TotpInput.vue +81 -0
  78. package/src/components/TwoFactorSetup.vue +113 -0
  79. package/src/components/VariableTokenInput.vue +111 -0
  80. package/src/components/WorkflowActionConfig.vue +287 -0
  81. package/src/components/WorkflowActionList.vue +350 -0
  82. package/src/components/WorkflowConditionBuilder.vue +265 -0
  83. package/src/components/WorkflowTriggerSelector.vue +125 -0
  84. package/src/composables/useChat.js +187 -0
  85. package/src/composables/useDebouncedSearch.js +38 -0
  86. package/src/composables/useI18n.js +80 -80
  87. package/src/composables/useKeyboardShortcuts.js +46 -46
  88. package/src/composables/useMentions.js +134 -0
  89. package/src/composables/usePluginExtensions.js +159 -67
  90. package/src/composables/usePluginHooks.js +65 -0
  91. package/src/composables/useRealtime.js +182 -0
  92. package/src/defineEscalatedPlugin.js +76 -50
  93. package/src/index.js +77 -28
  94. package/src/locales/ar.json +727 -0
  95. package/src/locales/de.json +678 -430
  96. package/src/locales/en.json +717 -430
  97. package/src/locales/es.json +679 -430
  98. package/src/locales/fr.json +680 -431
  99. package/src/locales/index.js +12 -2
  100. package/src/locales/it.json +727 -0
  101. package/src/locales/ja.json +727 -0
  102. package/src/locales/ko.json +727 -0
  103. package/src/locales/nl.json +727 -0
  104. package/src/locales/pl.json +727 -0
  105. package/src/locales/pt-BR.json +727 -0
  106. package/src/locales/ru.json +727 -0
  107. package/src/locales/tr.json +727 -0
  108. package/src/locales/zh-CN.json +727 -0
  109. package/src/pages/Admin/ApiTokens/Index.vue +381 -217
  110. package/src/pages/Admin/AuditLog/Index.vue +154 -0
  111. package/src/pages/Admin/BusinessHours/Form.vue +172 -0
  112. package/src/pages/Admin/BusinessHours/Index.vue +113 -0
  113. package/src/pages/Admin/CannedResponses/Index.vue +82 -25
  114. package/src/pages/Admin/Capacity/Index.vue +134 -0
  115. package/src/pages/Admin/CustomFields/Form.vue +195 -0
  116. package/src/pages/Admin/CustomFields/Index.vue +134 -0
  117. package/src/pages/Admin/CustomObjects/Form.vue +202 -0
  118. package/src/pages/Admin/CustomObjects/Index.vue +127 -0
  119. package/src/pages/Admin/CustomObjects/Records.vue +228 -0
  120. package/src/pages/Admin/Departments/Form.vue +35 -11
  121. package/src/pages/Admin/Departments/Index.vue +74 -21
  122. package/src/pages/Admin/EscalationRules/Form.vue +86 -25
  123. package/src/pages/Admin/EscalationRules/Index.vue +158 -37
  124. package/src/pages/Admin/Import/Connect.vue +95 -0
  125. package/src/pages/Admin/Import/Index.vue +102 -0
  126. package/src/pages/Admin/Import/Mapping.vue +110 -0
  127. package/src/pages/Admin/Import/Progress.vue +153 -0
  128. package/src/pages/Admin/Import/Review.vue +76 -0
  129. package/src/pages/Admin/KnowledgeBase/Articles/Form.vue +142 -0
  130. package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +195 -0
  131. package/src/pages/Admin/KnowledgeBase/Categories/Index.vue +193 -0
  132. package/src/pages/Admin/Macros/Index.vue +448 -287
  133. package/src/pages/Admin/Plugins/Index.vue +327 -197
  134. package/src/pages/Admin/Reports/AgentDetail.vue +132 -0
  135. package/src/pages/Admin/Reports/AgentMetrics.vue +115 -0
  136. package/src/pages/Admin/Reports/AgentRanking.vue +246 -0
  137. package/src/pages/Admin/Reports/Cohorts.vue +180 -0
  138. package/src/pages/Admin/Reports/Comparison.vue +141 -0
  139. package/src/pages/Admin/Reports/CsatReport.vue +127 -0
  140. package/src/pages/Admin/Reports/Dashboard.vue +226 -0
  141. package/src/pages/Admin/Reports/ResolutionTimes.vue +164 -0
  142. package/src/pages/Admin/Reports/ResponseTimes.vue +164 -0
  143. package/src/pages/Admin/Reports/SlaReport.vue +162 -0
  144. package/src/pages/Admin/Reports/SlaTrends.vue +183 -0
  145. package/src/pages/Admin/Reports.vue +206 -101
  146. package/src/pages/Admin/Roles/Form.vue +71 -0
  147. package/src/pages/Admin/Roles/Index.vue +122 -0
  148. package/src/pages/Admin/Settings/CsatSettings.vue +165 -0
  149. package/src/pages/Admin/Settings/DataRetention.vue +182 -0
  150. package/src/pages/Admin/Settings/EmailSettings.vue +243 -0
  151. package/src/pages/Admin/Settings/Sandbox.vue +135 -0
  152. package/src/pages/Admin/Settings/SsoSettings.vue +218 -0
  153. package/src/pages/Admin/Settings/TwoFactor.vue +110 -0
  154. package/src/pages/Admin/Settings.vue +847 -99
  155. package/src/pages/Admin/Skills/Form.vue +53 -0
  156. package/src/pages/Admin/Skills/Index.vue +94 -0
  157. package/src/pages/Admin/SlaPolicies/Form.vue +62 -19
  158. package/src/pages/Admin/SlaPolicies/Index.vue +85 -23
  159. package/src/pages/Admin/Statuses/Form.vue +114 -0
  160. package/src/pages/Admin/Statuses/Index.vue +153 -0
  161. package/src/pages/Admin/Tags/Index.vue +121 -35
  162. package/src/pages/Admin/Tickets/Index.vue +96 -9
  163. package/src/pages/Admin/Tickets/Show.vue +130 -37
  164. package/src/pages/Admin/Webhooks/DeliveryLog.vue +120 -0
  165. package/src/pages/Admin/Webhooks/Form.vue +110 -0
  166. package/src/pages/Admin/Webhooks/Index.vue +136 -0
  167. package/src/pages/Admin/Workflows/Builder.vue +207 -0
  168. package/src/pages/Admin/Workflows/Index.vue +235 -0
  169. package/src/pages/Admin/Workflows/Logs.vue +286 -0
  170. package/src/pages/Agent/Dashboard.vue +311 -186
  171. package/src/pages/Agent/TicketIndex.vue +80 -6
  172. package/src/pages/Agent/TicketShow.vue +399 -60
  173. package/src/pages/Customer/Create.vue +37 -10
  174. package/src/pages/Customer/Index.vue +9 -2
  175. package/src/pages/Customer/KnowledgeBase/Article.vue +129 -0
  176. package/src/pages/Customer/KnowledgeBase/Index.vue +151 -0
  177. package/src/pages/Customer/Show.vue +69 -63
  178. package/src/pages/Guest/Create.vue +40 -15
  179. package/src/pages/Guest/Show.vue +124 -93
  180. package/src/plugin.js +111 -1
  181. package/src/utils/constants.js +30 -0
  182. package/src/utils/formatting.js +35 -0
  183. package/src/utils/mentionHighlight.js +24 -0
  184. package/src/utils/sanitizeHtml.js +78 -64
  185. package/src/widget/EscalatedWidget.vue +804 -0
  186. package/src/widget/index.js +254 -0
  187. package/src/pages/Admin/Plugins.vue +0 -129
@@ -1,9 +1,11 @@
1
1
  <script setup>
2
2
  import EscalatedLayout from '../../components/EscalatedLayout.vue';
3
+ import PluginSlot from '../../components/PluginSlot.vue';
3
4
  import { useForm, usePage } from '@inertiajs/vue3';
4
5
  import { computed } from 'vue';
6
+ import { usePluginExtensions } from '../../composables/usePluginExtensions';
5
7
 
6
- const props = defineProps({ settings: Object });
8
+ const props = defineProps({ settings: Object, departments: Array });
7
9
  const page = usePage();
8
10
 
9
11
  const form = useForm({
@@ -26,6 +28,42 @@ const form = useForm({
26
28
  imap_username: props.settings.imap_username ?? '',
27
29
  imap_password: props.settings.imap_password ?? '',
28
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',
29
67
  });
30
68
 
31
69
  const webhookBaseUrl = computed(() => {
@@ -34,6 +72,8 @@ const webhookBaseUrl = computed(() => {
34
72
  return `${origin}/${prefix}/inbound`;
35
73
  });
36
74
 
75
+ const { getPageComponents } = usePluginExtensions();
76
+
37
77
  function submit() {
38
78
  form.post(route('escalated.admin.settings.update'));
39
79
  }
@@ -41,105 +81,184 @@ function submit() {
41
81
 
42
82
  <template>
43
83
  <EscalatedLayout title="Settings">
44
- <form @submit.prevent="submit" class="mx-auto max-w-2xl space-y-6">
84
+ <form class="mx-auto max-w-2xl space-y-6" @submit.prevent="submit">
45
85
  <!-- General -->
46
- <div class="rounded-xl border border-white/[0.06] bg-neutral-900/60 p-6">
47
- <h3 class="mb-5 text-sm font-semibold text-white">General</h3>
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>
48
88
  <div class="space-y-5">
49
89
  <label class="flex items-center justify-between">
50
90
  <div>
51
- <span class="text-sm font-medium text-neutral-200">Guest Tickets</span>
52
- <p class="mt-0.5 text-xs text-neutral-500">Allow visitors to submit tickets without signing in</p>
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>
53
97
  </div>
54
- <button type="button" @click="form.guest_tickets_enabled = !form.guest_tickets_enabled"
55
- :class="['relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
56
- form.guest_tickets_enabled ? 'bg-emerald-500' : 'bg-neutral-700']">
57
- <span :class="['pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
58
- form.guest_tickets_enabled ? 'translate-x-5' : 'translate-x-0']" />
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
+ />
59
112
  </button>
60
113
  </label>
61
114
 
62
115
  <label class="flex items-center justify-between">
63
116
  <div>
64
- <span class="text-sm font-medium text-neutral-200">Allow Customer Close</span>
65
- <p class="mt-0.5 text-xs text-neutral-500">Let customers close their own tickets</p>
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>
66
123
  </div>
67
- <button type="button" @click="form.allow_customer_close = !form.allow_customer_close"
68
- :class="['relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
69
- form.allow_customer_close ? 'bg-emerald-500' : 'bg-neutral-700']">
70
- <span :class="['pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
71
- form.allow_customer_close ? 'translate-x-5' : 'translate-x-0']" />
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
+ />
72
138
  </button>
73
139
  </label>
74
140
 
75
141
  <div>
76
- <label class="block text-sm font-medium text-neutral-200">Auto-close resolved tickets after</label>
77
- <p class="mt-0.5 text-xs text-neutral-500">Days after resolution before auto-closing (0 to disable)</p>
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>
78
148
  <div class="mt-2 flex items-center gap-2">
79
- <input v-model.number="form.auto_close_resolved_after_days" type="number" min="0" max="365"
80
- class="w-24 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
81
- <span class="text-sm text-neutral-500">days</span>
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>
82
157
  </div>
83
158
  </div>
84
159
  </div>
85
160
  </div>
86
161
 
87
162
  <!-- Limits -->
88
- <div class="rounded-xl border border-white/[0.06] bg-neutral-900/60 p-6">
89
- <h3 class="mb-5 text-sm font-semibold text-white">Limits</h3>
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>
90
165
  <div class="space-y-5">
91
166
  <div>
92
- <label class="block text-sm font-medium text-neutral-200">Max attachments per reply</label>
93
- <input v-model.number="form.max_attachments_per_reply" type="number" min="1" max="20"
94
- class="mt-2 w-24 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
95
177
  </div>
96
178
  <div>
97
- <label class="block text-sm font-medium text-neutral-200">Max attachment size</label>
179
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]"
180
+ >Max attachment size</label
181
+ >
98
182
  <div class="mt-2 flex items-center gap-2">
99
- <input v-model.number="form.max_attachment_size_kb" type="number" min="512" max="102400" step="512"
100
- class="w-32 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
101
- <span class="text-sm text-neutral-500">KB ({{ Math.round(form.max_attachment_size_kb / 1024) }} MB)</span>
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
+ >
102
194
  </div>
103
195
  </div>
104
196
  </div>
105
197
  </div>
106
198
 
107
199
  <!-- Tickets -->
108
- <div class="rounded-xl border border-white/[0.06] bg-neutral-900/60 p-6">
109
- <h3 class="mb-5 text-sm font-semibold text-white">Tickets</h3>
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>
110
202
  <div class="space-y-5">
111
203
  <div>
112
- <label class="block text-sm font-medium text-neutral-200">Reference Prefix</label>
113
- <p class="mt-0.5 text-xs text-neutral-500">Prefix for ticket references (e.g. ESC produces ESC-00001)</p>
114
- <input v-model="form.ticket_reference_prefix" type="text" maxlength="10"
115
- class="mt-2 w-32 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 uppercase focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
116
216
  </div>
117
217
  </div>
118
218
  </div>
119
219
 
120
220
  <!-- Inbound Email -->
121
- <div class="rounded-xl border border-white/[0.06] bg-neutral-900/60 p-6">
122
- <h3 class="mb-5 text-sm font-semibold text-white">Inbound Email</h3>
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>
123
223
  <div class="space-y-5">
124
224
  <label class="flex items-center justify-between">
125
225
  <div>
126
- <span class="text-sm font-medium text-neutral-200">Inbound Email</span>
127
- <p class="mt-0.5 text-xs text-neutral-500">Allow creating and replying to tickets via email</p>
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>
128
232
  </div>
129
- <button type="button" @click="form.inbound_email_enabled = !form.inbound_email_enabled"
130
- :class="['relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
131
- form.inbound_email_enabled ? 'bg-emerald-500' : 'bg-neutral-700']">
132
- <span :class="['pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition-transform',
133
- form.inbound_email_enabled ? 'translate-x-5' : 'translate-x-0']" />
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
+ />
134
247
  </button>
135
248
  </label>
136
249
 
137
250
  <template v-if="form.inbound_email_enabled">
138
251
  <div>
139
- <label class="block text-sm font-medium text-neutral-200">Email Provider</label>
140
- <p class="mt-0.5 text-xs text-neutral-500">How inbound emails are received</p>
141
- <select v-model="form.inbound_email_adapter"
142
- class="mt-2 w-48 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10">
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
+ >
143
262
  <option value="mailgun">Mailgun</option>
144
263
  <option value="postmark">Postmark</option>
145
264
  <option value="ses">AWS SES</option>
@@ -148,79 +267,150 @@ function submit() {
148
267
  </div>
149
268
 
150
269
  <div>
151
- <label class="block text-sm font-medium text-neutral-200">Support Email Address</label>
152
- <p class="mt-0.5 text-xs text-neutral-500">The email address customers will send emails to (e.g. support@example.com)</p>
153
- <input v-model="form.inbound_email_address" type="email" placeholder="support@example.com"
154
- class="mt-2 w-full max-w-sm rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
155
282
  </div>
156
283
 
157
284
  <!-- Mailgun -->
158
285
  <template v-if="form.inbound_email_adapter === 'mailgun'">
159
286
  <div>
160
- <label class="block text-sm font-medium text-neutral-200">Signing Key</label>
161
- <input v-model="form.mailgun_signing_key" type="password"
162
- class="mt-2 w-full max-w-sm rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
163
295
  </div>
164
296
  <div>
165
- <label class="block text-sm font-medium text-neutral-200">Webhook URL</label>
166
- <p class="mt-0.5 text-xs text-neutral-500">Configure this URL in your Mailgun dashboard</p>
167
- <input :value="`${webhookBaseUrl}/mailgun`" type="text" readonly
168
- class="mt-2 w-full max-w-lg rounded-lg border border-white/10 bg-neutral-950/50 px-3 py-2 text-sm text-neutral-400 select-all cursor-default focus:outline-none" />
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
+ />
169
309
  </div>
170
310
  </template>
171
311
 
172
312
  <!-- Postmark -->
173
313
  <template v-if="form.inbound_email_adapter === 'postmark'">
174
314
  <div>
175
- <label class="block text-sm font-medium text-neutral-200">Inbound Token</label>
176
- <input v-model="form.postmark_inbound_token" type="password"
177
- class="mt-2 w-full max-w-sm rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
178
323
  </div>
179
324
  <div>
180
- <label class="block text-sm font-medium text-neutral-200">Webhook URL</label>
181
- <p class="mt-0.5 text-xs text-neutral-500">Configure this URL in your Postmark server settings</p>
182
- <input :value="`${webhookBaseUrl}/postmark`" type="text" readonly
183
- class="mt-2 w-full max-w-lg rounded-lg border border-white/10 bg-neutral-950/50 px-3 py-2 text-sm text-neutral-400 select-all cursor-default focus:outline-none" />
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
+ />
184
337
  </div>
185
338
  </template>
186
339
 
187
340
  <!-- AWS SES -->
188
341
  <template v-if="form.inbound_email_adapter === 'ses'">
189
342
  <div>
190
- <label class="block text-sm font-medium text-neutral-200">Region</label>
191
- <input v-model="form.ses_region" type="text" placeholder="us-east-1"
192
- class="mt-2 w-48 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
193
352
  </div>
194
353
  <div>
195
- <label class="block text-sm font-medium text-neutral-200">Topic ARN</label>
196
- <input v-model="form.ses_topic_arn" type="text" placeholder="arn:aws:sns:us-east-1:123456789:ses-inbound"
197
- class="mt-2 w-full max-w-lg rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
198
363
  </div>
199
364
  <div>
200
- <label class="block text-sm font-medium text-neutral-200">Webhook URL</label>
201
- <p class="mt-0.5 text-xs text-neutral-500">Configure this URL as your SNS subscription endpoint</p>
202
- <input :value="`${webhookBaseUrl}/ses`" type="text" readonly
203
- class="mt-2 w-full max-w-lg rounded-lg border border-white/10 bg-neutral-950/50 px-3 py-2 text-sm text-neutral-400 select-all cursor-default focus:outline-none" />
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
+ />
204
377
  </div>
205
378
  </template>
206
379
 
207
380
  <!-- IMAP -->
208
381
  <template v-if="form.inbound_email_adapter === 'imap'">
209
382
  <div>
210
- <label class="block text-sm font-medium text-neutral-200">Host</label>
211
- <input v-model="form.imap_host" type="text" placeholder="imap.example.com"
212
- class="mt-2 w-full max-w-sm rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
213
392
  </div>
214
393
  <div class="flex items-end gap-4">
215
394
  <div>
216
- <label class="block text-sm font-medium text-neutral-200">Port</label>
217
- <input v-model.number="form.imap_port" type="number" min="1" max="65535"
218
- class="mt-2 w-24 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
219
405
  </div>
220
406
  <div>
221
- <label class="block text-sm font-medium text-neutral-200">Encryption</label>
222
- <select v-model="form.imap_encryption"
223
- class="mt-2 w-28 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10">
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
+ >
224
414
  <option value="ssl">SSL</option>
225
415
  <option value="tls">TLS</option>
226
416
  <option value="none">None</option>
@@ -228,30 +418,588 @@ function submit() {
228
418
  </div>
229
419
  </div>
230
420
  <div>
231
- <label class="block text-sm font-medium text-neutral-200">Username</label>
232
- <input v-model="form.imap_username" type="text"
233
- class="mt-2 w-full max-w-sm rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
234
429
  </div>
235
430
  <div>
236
- <label class="block text-sm font-medium text-neutral-200">Password</label>
237
- <input v-model="form.imap_password" type="password"
238
- class="mt-2 w-full max-w-sm rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
239
439
  </div>
240
440
  <div>
241
- <label class="block text-sm font-medium text-neutral-200">Mailbox</label>
242
- <input v-model="form.imap_mailbox" type="text" placeholder="INBOX"
243
- class="mt-2 w-48 rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
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
+ />
244
450
  </div>
245
451
  </template>
246
452
  </template>
247
453
  </div>
248
454
  </div>
249
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
+
250
995
  <!-- Submit -->
251
996
  <div class="flex items-center justify-end gap-3">
252
997
  <span v-if="form.recentlySuccessful" class="text-sm text-emerald-400">Saved.</span>
253
- <button type="submit" :disabled="form.processing"
254
- class="rounded-lg bg-gradient-to-r from-cyan-500 to-violet-500 px-5 py-2 text-sm font-medium text-white shadow-lg shadow-black/20 transition-all hover:from-cyan-400 hover:to-violet-400 disabled:opacity-50">
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
+ >
255
1003
  {{ form.processing ? 'Saving...' : 'Save Settings' }}
256
1004
  </button>
257
1005
  </div>