@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
@@ -2,13 +2,15 @@
2
2
  import { computed, inject, provide } from 'vue';
3
3
  import { usePage, Link } from '@inertiajs/vue3';
4
4
  import { usePluginExtensions } from '../composables/usePluginExtensions';
5
+ import ActiveChatsPanel from './ActiveChatsPanel.vue';
5
6
 
6
- const props = defineProps({
7
+ defineProps({
7
8
  title: { type: String, default: 'Support' },
8
9
  });
9
10
 
10
11
  const page = usePage();
11
12
  const hostLayout = inject('escalated-layout', null);
13
+ const panelConfig = inject('escalated-panel', { appName: 'Escalated', logo: null, mode: 'dark' });
12
14
 
13
15
  const isAgent = computed(() => page.props.escalated?.is_agent);
14
16
  const isAdmin = computed(() => page.props.escalated?.is_admin);
@@ -20,7 +22,14 @@ const prefix = computed(() => {
20
22
  const currentUrl = computed(() => page.url);
21
23
  const isAdminSection = computed(() => currentUrl.value?.includes('/admin'));
22
24
  const isAgentSection = computed(() => currentUrl.value?.includes('/agent'));
23
- const isDark = computed(() => isAdminSection.value || isAgentSection.value);
25
+ const isPanel = computed(() => isAdminSection.value || isAgentSection.value);
26
+ const isDark = computed(() => isPanel.value && panelConfig.mode !== 'light');
27
+ const showPoweredBy = computed(() => page.props.escalated?.show_powered_by !== false);
28
+ const kbEnabled = computed(() => page.props.escalated?.knowledge_base_enabled !== false);
29
+ const kbPublic = computed(() => page.props.escalated?.knowledge_base_public !== false);
30
+ const chatEnabled = computed(() => page.props.escalated?.chat_enabled === true);
31
+ const activeChats = computed(() => page.props.escalated?.active_chats || []);
32
+ const agentId = computed(() => page.props.auth?.user?.id);
24
33
 
25
34
  provide('esc-dark', isDark);
26
35
 
@@ -29,21 +38,148 @@ const { menuItems: pluginMenuItems } = usePluginExtensions();
29
38
  const adminLinks = computed(() => {
30
39
  const p = prefix.value;
31
40
  const coreLinks = [
32
- { href: `${p}/admin/reports`, label: 'Reports', icon: 'M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z', position: 10 },
33
- { href: `${p}/admin/tickets`, label: 'Tickets', icon: 'M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 010 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 010-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375z', position: 20 },
34
- { href: `${p}/admin/departments`, label: 'Departments', icon: 'M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75zM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-8.25zM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-2.25z', position: 30 },
35
- { href: `${p}/admin/sla-policies`, label: 'SLA Policies', icon: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z', position: 40 },
36
- { href: `${p}/admin/escalation-rules`, label: 'Escalation Rules', icon: 'M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12', position: 50 },
37
- { href: `${p}/admin/tags`, label: 'Tags', icon: 'M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z M6 6h.008v.008H6V6z', position: 60 },
38
- { href: `${p}/admin/canned-responses`, label: 'Canned Responses', icon: 'M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z', position: 70 },
39
- { href: `${p}/admin/macros`, label: 'Macros', icon: 'M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z', position: 80 },
40
- { href: `${p}/admin/api-tokens`, label: 'API Tokens', icon: 'M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z', position: 90 },
41
- { href: `${p}/admin/plugins`, label: 'Plugins', icon: 'M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z', position: 95 },
42
- { href: `${p}/admin/settings`, label: 'Settings', icon: 'M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.204-.107-.397.165-.71.505-.781.93l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894zM15 12a3 3 0 11-6 0 3 3 0 016 0z', position: 100 },
41
+ {
42
+ href: `${p}/admin/reports`,
43
+ label: 'Reports',
44
+ icon: 'M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z',
45
+ position: 10,
46
+ },
47
+ {
48
+ href: `${p}/admin/tickets`,
49
+ label: 'Tickets',
50
+ icon: 'M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 010 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 010-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375z',
51
+ position: 20,
52
+ },
53
+ {
54
+ href: `${p}/admin/departments`,
55
+ label: 'Departments',
56
+ icon: 'M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75zM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-8.25zM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-2.25z',
57
+ position: 30,
58
+ },
59
+ {
60
+ href: `${p}/admin/sla-policies`,
61
+ label: 'SLA Policies',
62
+ icon: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z',
63
+ position: 40,
64
+ },
65
+ {
66
+ href: `${p}/admin/kb-articles`,
67
+ label: 'Knowledge Base',
68
+ icon: 'M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25',
69
+ position: 45,
70
+ },
71
+ {
72
+ href: `${p}/admin/escalation-rules`,
73
+ label: 'Escalation Rules',
74
+ icon: 'M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12',
75
+ position: 50,
76
+ },
77
+ {
78
+ href: `${p}/admin/workflows`,
79
+ label: 'Workflows',
80
+ icon: 'M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z M15 12a3 3 0 11-6 0 3 3 0 016 0z',
81
+ position: 53,
82
+ },
83
+ {
84
+ href: `${p}/admin/webhooks`,
85
+ label: 'Webhooks',
86
+ icon: 'M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244',
87
+ position: 54,
88
+ },
89
+ {
90
+ href: `${p}/admin/tags`,
91
+ label: 'Tags',
92
+ icon: 'M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z M6 6h.008v.008H6V6z',
93
+ position: 60,
94
+ },
95
+ {
96
+ href: `${p}/admin/custom-fields`,
97
+ label: 'Custom Fields',
98
+ icon: 'M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 010 3.75H5.625a1.875 1.875 0 010-3.75z',
99
+ position: 62,
100
+ },
101
+ {
102
+ href: `${p}/admin/statuses`,
103
+ label: 'Statuses',
104
+ icon: 'M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
105
+ position: 64,
106
+ },
107
+ {
108
+ href: `${p}/admin/canned-responses`,
109
+ label: 'Canned Responses',
110
+ icon: 'M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z',
111
+ position: 70,
112
+ },
113
+ {
114
+ href: `${p}/admin/macros`,
115
+ label: 'Macros',
116
+ icon: 'M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z',
117
+ position: 80,
118
+ },
119
+ {
120
+ href: `${p}/admin/skills`,
121
+ label: 'Skills',
122
+ icon: 'M4.26 10.147a60.436 60.436 0 00-.491 6.347A48.627 48.627 0 0112 20.904a48.627 48.627 0 018.232-4.41 60.46 60.46 0 00-.491-6.347m-15.482 0a50.57 50.57 0 00-2.658-.813A59.905 59.905 0 0112 3.493a59.902 59.902 0 0110.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.697 50.697 0 0112 13.489a50.702 50.702 0 017.74-3.342',
123
+ position: 82,
124
+ },
125
+ {
126
+ href: `${p}/admin/roles`,
127
+ label: 'Roles',
128
+ icon: 'M18 18.72a9.094 9.094 0 003.741-.479 3 3 0 00-4.682-2.72m.94 3.198l.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0112 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 016 18.719m12 0a5.971 5.971 0 00-.941-3.197m0 0A5.995 5.995 0 0012 12.75a5.995 5.995 0 00-5.058 2.772m0 0a3 3 0 00-4.681 2.72 8.986 8.986 0 003.74.477m.94-3.197a5.971 5.971 0 00-.94 3.197M15 6.75a3 3 0 11-6 0 3 3 0 016 0zm6 3a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm-13.5 0a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z',
129
+ position: 84,
130
+ },
131
+ {
132
+ href: `${p}/admin/capacity`,
133
+ label: 'Capacity',
134
+ icon: 'M3 7.5L7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5',
135
+ position: 85,
136
+ },
137
+ {
138
+ href: `${p}/admin/business-hours`,
139
+ label: 'Business Hours',
140
+ icon: 'M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5',
141
+ position: 86,
142
+ },
143
+ {
144
+ href: `${p}/admin/audit-log`,
145
+ label: 'Audit Log',
146
+ icon: 'M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z',
147
+ position: 88,
148
+ },
149
+ {
150
+ href: `${p}/admin/api-tokens`,
151
+ label: 'API Tokens',
152
+ icon: 'M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z',
153
+ position: 90,
154
+ },
155
+ {
156
+ href: `${p}/admin/custom-objects`,
157
+ label: 'Custom Objects',
158
+ icon: 'M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125',
159
+ position: 92,
160
+ },
161
+ {
162
+ href: `${p}/admin/plugins`,
163
+ label: 'Plugins',
164
+ icon: 'M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z',
165
+ position: 95,
166
+ },
167
+ {
168
+ href: `${p}/admin/import`,
169
+ label: 'Import',
170
+ icon: 'M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5',
171
+ position: 97,
172
+ },
173
+ {
174
+ href: `${p}/admin/settings`,
175
+ label: 'Settings',
176
+ icon: 'M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.204-.107-.397.165-.71.505-.781.93l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894zM15 12a3 3 0 11-6 0 3 3 0 016 0z',
177
+ position: 100,
178
+ },
43
179
  ];
44
180
 
45
181
  // Merge core admin links with plugin-provided menu items
46
- const pluginLinks = pluginMenuItems.value.map(item => ({
182
+ const pluginLinks = pluginMenuItems.value.map((item) => ({
47
183
  href: item.route,
48
184
  label: item.label,
49
185
  icon: item.icon,
@@ -56,8 +192,16 @@ const adminLinks = computed(() => {
56
192
  });
57
193
 
58
194
  const agentLinks = computed(() => [
59
- { href: `${prefix.value}/agent`, label: 'Dashboard', icon: 'M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25' },
60
- { href: `${prefix.value}/agent/tickets`, label: 'Tickets', icon: 'M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 010 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 010-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375z' },
195
+ {
196
+ href: `${prefix.value}/agent`,
197
+ label: 'Dashboard',
198
+ icon: 'M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25',
199
+ },
200
+ {
201
+ href: `${prefix.value}/agent/tickets`,
202
+ label: 'Tickets',
203
+ icon: 'M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 010 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 010-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375z',
204
+ },
61
205
  ]);
62
206
 
63
207
  const userName = computed(() => page.props.auth?.user?.name || 'User');
@@ -71,102 +215,242 @@ function isActive(href) {
71
215
 
72
216
  <template>
73
217
  <!-- MODE 1: Admin — dark sidebar layout -->
74
- <div v-if="isAdminSection" class="flex min-h-screen bg-black" style="color-scheme: dark">
218
+ <div
219
+ v-if="isAdminSection"
220
+ class="flex min-h-screen bg-[var(--esc-panel-bg)]"
221
+ :style="{ colorScheme: panelConfig.mode === 'light' ? 'light' : 'dark' }"
222
+ >
75
223
  <!-- Sidebar -->
76
- <aside class="fixed inset-y-0 left-0 z-30 flex w-64 flex-col border-r border-white/[0.06] bg-neutral-950">
224
+ <aside
225
+ class="fixed inset-y-0 left-0 z-30 flex w-64 flex-col border-r border-[var(--esc-panel-border)] bg-[var(--esc-panel-sidebar-bg)]"
226
+ >
77
227
  <!-- Logo -->
78
228
  <div class="flex h-16 items-center gap-3 px-5">
79
- <div class="flex h-9 w-9 items-center justify-center rounded-lg bg-white/10">
80
- <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
81
- <defs><linearGradient id="esc-rainbow-admin" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#f97316"/><stop offset="30%" stop-color="#eab308"/><stop offset="50%" stop-color="#22c55e"/><stop offset="70%" stop-color="#3b82f6"/><stop offset="100%" stop-color="#8b5cf6"/></linearGradient></defs>
82
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rainbow-admin)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rainbow-admin)"/></g>
229
+ <div class="flex h-9 w-9 items-center justify-center rounded-lg bg-[var(--esc-panel-border-input)]">
230
+ <img
231
+ v-if="typeof panelConfig.logo === 'string'"
232
+ :src="panelConfig.logo"
233
+ class="h-5 w-5"
234
+ alt="Escalated logo"
235
+ />
236
+ <component :is="panelConfig.logo" v-else-if="panelConfig.logo" class="h-5 w-5" />
237
+ <svg
238
+ v-else
239
+ class="h-5 w-5"
240
+ viewBox="0 0 24 24"
241
+ fill="none"
242
+ stroke-width="2.5"
243
+ stroke-linecap="round"
244
+ stroke-linejoin="round"
245
+ >
246
+ <defs>
247
+ <linearGradient id="esc-rainbow-admin" x1="0%" y1="0%" x2="100%" y2="100%">
248
+ <stop offset="0%" stop-color="#f97316" />
249
+ <stop offset="30%" stop-color="#eab308" />
250
+ <stop offset="50%" stop-color="#22c55e" />
251
+ <stop offset="70%" stop-color="#3b82f6" />
252
+ <stop offset="100%" stop-color="#8b5cf6" />
253
+ </linearGradient>
254
+ </defs>
255
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)">
256
+ <polyline points="17 11 12 6 7 11" stroke="url(#esc-rainbow-admin)" />
257
+ <polyline points="17 18 12 13 7 18" stroke="url(#esc-rainbow-admin)" />
258
+ </g>
83
259
  </svg>
84
260
  </div>
85
261
  <div>
86
- <span class="text-sm font-bold text-white tracking-wide">Escalated</span>
87
- <span class="ml-1.5 rounded bg-white/[0.08] px-1.5 py-0.5 text-[10px] font-semibold text-neutral-500">ADMIN</span>
262
+ <span class="text-sm font-bold text-[var(--esc-panel-text)] tracking-wide">{{
263
+ panelConfig.appName
264
+ }}</span>
265
+ <span
266
+ class="ml-1.5 rounded bg-[var(--esc-panel-active)] px-1.5 py-0.5 text-[10px] font-semibold text-[var(--esc-panel-text-muted)]"
267
+ >ADMIN</span
268
+ >
88
269
  </div>
89
270
  </div>
90
271
 
91
272
  <!-- Nav -->
92
- <nav class="mt-1 flex-1 space-y-0.5 overflow-y-auto px-3">
93
- <Link v-for="link in adminLinks" :key="link.href" :href="link.href"
94
- :class="['group flex items-center gap-3 rounded-lg px-3 py-2 text-[13px] font-medium transition-all',
95
- isActive(link.href)
96
- ? 'bg-white/[0.08] text-white'
97
- : 'text-neutral-500 hover:bg-white/[0.04] hover:text-neutral-300']">
98
- <svg :class="['h-[18px] w-[18px] shrink-0', isActive(link.href) ? 'text-white' : 'text-neutral-600 group-hover:text-neutral-400']"
99
- fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
273
+ <nav role="navigation" aria-label="Admin navigation" class="mt-1 flex-1 space-y-0.5 overflow-y-auto px-3">
274
+ <Link
275
+ v-for="link in adminLinks"
276
+ :key="link.href"
277
+ :href="link.href"
278
+ :aria-current="isActive(link.href) ? 'page' : undefined"
279
+ :class="[
280
+ 'group flex items-center gap-3 rounded-lg px-3 py-2 text-[13px] font-medium transition-all',
281
+ isActive(link.href)
282
+ ? 'bg-[var(--esc-panel-active)] text-[var(--esc-panel-text)]'
283
+ : 'text-[var(--esc-panel-text-muted)] hover:bg-[var(--esc-panel-hover)] hover:text-[var(--esc-panel-text-secondary)]',
284
+ ]"
285
+ >
286
+ <svg
287
+ :class="[
288
+ 'h-[18px] w-[18px] shrink-0',
289
+ isActive(link.href)
290
+ ? 'text-[var(--esc-panel-text)]'
291
+ : 'text-[var(--esc-panel-text-muted)] group-hover:text-[var(--esc-panel-text-tertiary)]',
292
+ ]"
293
+ fill="none"
294
+ stroke="currentColor"
295
+ stroke-width="1.5"
296
+ viewBox="0 0 24 24"
297
+ >
100
298
  <path stroke-linecap="round" stroke-linejoin="round" :d="link.icon" />
101
299
  </svg>
102
300
  {{ link.label }}
103
- <span v-if="link.badge"
104
- class="ml-auto inline-flex h-5 min-w-[20px] items-center justify-center rounded-full bg-cyan-500/20 px-1.5 text-[10px] font-semibold text-cyan-400">
301
+ <span
302
+ v-if="link.badge"
303
+ class="ml-auto inline-flex h-5 min-w-[20px] items-center justify-center rounded-full bg-[var(--esc-panel-accent)]/20 px-1.5 text-[10px] font-semibold text-[var(--esc-panel-accent)]"
304
+ >
105
305
  {{ link.badge }}
106
306
  </span>
107
307
  </Link>
108
308
  </nav>
109
309
 
110
310
  <!-- Bottom section -->
111
- <div class="border-t border-white/[0.06] p-3">
112
- <Link v-if="isAgent" :href="`${prefix}/agent`"
113
- class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] text-neutral-500 transition-colors hover:bg-white/[0.04] hover:text-neutral-300">
114
- <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3" /></svg>
311
+ <div class="border-t border-[var(--esc-panel-border)] p-3">
312
+ <Link
313
+ v-if="isAgent"
314
+ :href="`${prefix}/agent`"
315
+ class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] text-[var(--esc-panel-text-muted)] transition-colors hover:bg-[var(--esc-panel-hover)] hover:text-[var(--esc-panel-text-secondary)]"
316
+ >
317
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
318
+ <path
319
+ stroke-linecap="round"
320
+ stroke-linejoin="round"
321
+ d="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3"
322
+ />
323
+ </svg>
115
324
  Agent Panel
116
325
  </Link>
117
- <Link :href="prefix"
118
- class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] text-neutral-500 transition-colors hover:bg-white/[0.04] hover:text-neutral-300">
119
- <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" /></svg>
326
+ <Link
327
+ :href="prefix"
328
+ class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] text-[var(--esc-panel-text-muted)] transition-colors hover:bg-[var(--esc-panel-hover)] hover:text-[var(--esc-panel-text-secondary)]"
329
+ >
330
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
331
+ <path
332
+ stroke-linecap="round"
333
+ stroke-linejoin="round"
334
+ d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9"
335
+ />
336
+ </svg>
120
337
  Back to App
121
338
  </Link>
122
339
 
123
340
  <!-- User -->
124
- <div class="mt-2 flex items-center gap-3 rounded-lg bg-white/[0.03] px-3 py-2.5">
125
- <div class="flex h-7 w-7 items-center justify-center rounded-md bg-white/[0.08] text-xs font-semibold text-neutral-400">
341
+ <div class="mt-2 flex items-center gap-3 rounded-lg bg-[var(--esc-panel-hover)] px-3 py-2.5">
342
+ <div
343
+ class="flex h-7 w-7 items-center justify-center rounded-md bg-[var(--esc-panel-active)] text-xs font-semibold text-[var(--esc-panel-text-tertiary)]"
344
+ >
126
345
  {{ userInitial }}
127
346
  </div>
128
- <span class="text-sm text-neutral-400">{{ userName }}</span>
347
+ <span class="text-sm text-[var(--esc-panel-text-tertiary)]">{{ userName }}</span>
129
348
  </div>
130
349
  </div>
350
+
351
+ <!-- Powered by -->
352
+ <div v-if="showPoweredBy" class="px-4 pb-3">
353
+ <a
354
+ href="https://escalated.dev"
355
+ target="_blank"
356
+ rel="noopener noreferrer"
357
+ class="flex items-center justify-center gap-1.5 rounded-lg px-2 py-1.5 text-[11px] text-[var(--esc-panel-text-muted)] opacity-60 transition-opacity hover:opacity-100"
358
+ >
359
+ <img
360
+ :src="
361
+ isDark
362
+ ? 'https://escalated.dev/brand/logo-icon-dark.svg'
363
+ : 'https://escalated.dev/brand/logo-icon-white.svg'
364
+ "
365
+ alt="Escalated logo"
366
+ class="h-3.5 w-3.5"
367
+ />
368
+ Powered by Escalated
369
+ </a>
370
+ </div>
131
371
  </aside>
132
372
 
133
373
  <!-- Main content -->
134
374
  <div class="flex flex-1 flex-col pl-64">
135
375
  <!-- Top bar -->
136
- <header class="sticky top-0 z-20 flex h-14 items-center border-b border-white/[0.06] bg-black/80 px-6 backdrop-blur-xl">
137
- <h1 class="text-sm font-semibold text-white">{{ title }}</h1>
376
+ <header
377
+ role="banner"
378
+ class="sticky top-0 z-20 flex h-14 items-center border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-topbar-bg)] px-6 backdrop-blur-xl"
379
+ >
380
+ <h1 class="text-sm font-semibold text-[var(--esc-panel-text)]">{{ title }}</h1>
138
381
  </header>
139
382
 
140
383
  <!-- Page content -->
141
- <main class="flex-1 p-6">
384
+ <main role="main" class="flex-1 p-6">
142
385
  <slot />
143
386
  </main>
144
387
  </div>
145
388
  </div>
146
389
 
147
390
  <!-- MODE 2: Agent — dark top-nav layout -->
148
- <div v-else-if="isAgentSection" class="min-h-screen bg-black" style="color-scheme: dark">
391
+ <div
392
+ v-else-if="isAgentSection"
393
+ class="min-h-screen bg-[var(--esc-panel-bg)]"
394
+ :style="{ colorScheme: panelConfig.mode === 'light' ? 'light' : 'dark' }"
395
+ >
149
396
  <!-- Top nav -->
150
- <nav class="sticky top-0 z-30 border-b border-white/[0.06] bg-neutral-950/95 backdrop-blur-xl">
397
+ <nav
398
+ class="sticky top-0 z-30 border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-sidebar-bg)] backdrop-blur-xl"
399
+ >
151
400
  <div class="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
152
401
  <!-- Left: branding -->
153
402
  <div class="flex items-center gap-3">
154
- <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-white/10">
155
- <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
156
- <defs><linearGradient id="esc-rainbow-agent" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#f97316"/><stop offset="30%" stop-color="#eab308"/><stop offset="50%" stop-color="#22c55e"/><stop offset="70%" stop-color="#3b82f6"/><stop offset="100%" stop-color="#8b5cf6"/></linearGradient></defs>
157
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rainbow-agent)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rainbow-agent)"/></g>
403
+ <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-[var(--esc-panel-border-input)]">
404
+ <img
405
+ v-if="typeof panelConfig.logo === 'string'"
406
+ :src="panelConfig.logo"
407
+ class="h-4 w-4"
408
+ alt="Escalated logo"
409
+ />
410
+ <component :is="panelConfig.logo" v-else-if="panelConfig.logo" class="h-4 w-4" />
411
+ <svg
412
+ v-else
413
+ class="h-4 w-4"
414
+ viewBox="0 0 24 24"
415
+ fill="none"
416
+ stroke-width="2.5"
417
+ stroke-linecap="round"
418
+ stroke-linejoin="round"
419
+ >
420
+ <defs>
421
+ <linearGradient id="esc-rainbow-agent" x1="0%" y1="0%" x2="100%" y2="100%">
422
+ <stop offset="0%" stop-color="#f97316" />
423
+ <stop offset="30%" stop-color="#eab308" />
424
+ <stop offset="50%" stop-color="#22c55e" />
425
+ <stop offset="70%" stop-color="#3b82f6" />
426
+ <stop offset="100%" stop-color="#8b5cf6" />
427
+ </linearGradient>
428
+ </defs>
429
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)">
430
+ <polyline points="17 11 12 6 7 11" stroke="url(#esc-rainbow-agent)" />
431
+ <polyline points="17 18 12 13 7 18" stroke="url(#esc-rainbow-agent)" />
432
+ </g>
158
433
  </svg>
159
434
  </div>
160
- <span class="text-sm font-bold text-white tracking-wide">Escalated</span>
435
+ <span class="text-sm font-bold text-[var(--esc-panel-text)] tracking-wide">{{
436
+ panelConfig.appName
437
+ }}</span>
161
438
  </div>
162
439
 
163
440
  <!-- Center: nav links -->
164
441
  <div class="flex items-center gap-1">
165
- <Link v-for="link in agentLinks" :key="link.href" :href="link.href"
166
- :class="['flex items-center gap-2 rounded-lg px-3.5 py-1.5 text-[13px] font-medium transition-all',
167
- isActive(link.href)
168
- ? 'bg-white/[0.08] text-white'
169
- : 'text-neutral-500 hover:bg-white/[0.04] hover:text-neutral-300']">
442
+ <Link
443
+ v-for="link in agentLinks"
444
+ :key="link.href"
445
+ :href="link.href"
446
+ :aria-current="isActive(link.href) ? 'page' : undefined"
447
+ :class="[
448
+ 'flex items-center gap-2 rounded-lg px-3.5 py-1.5 text-[13px] font-medium transition-all',
449
+ isActive(link.href)
450
+ ? 'bg-[var(--esc-panel-active)] text-[var(--esc-panel-text)]'
451
+ : 'text-[var(--esc-panel-text-muted)] hover:bg-[var(--esc-panel-hover)] hover:text-[var(--esc-panel-text-secondary)]',
452
+ ]"
453
+ >
170
454
  <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
171
455
  <path stroke-linecap="round" stroke-linejoin="round" :d="link.icon" />
172
456
  </svg>
@@ -176,20 +460,27 @@ function isActive(href) {
176
460
 
177
461
  <!-- Right: user + links -->
178
462
  <div class="flex items-center gap-3">
179
- <Link v-if="isAdmin" :href="`${prefix}/admin/reports`"
180
- class="text-[13px] text-neutral-500 transition-colors hover:text-white">
463
+ <Link
464
+ v-if="isAdmin"
465
+ :href="`${prefix}/admin/reports`"
466
+ class="text-[13px] text-[var(--esc-panel-text-muted)] transition-colors hover:text-[var(--esc-panel-text)]"
467
+ >
181
468
  Admin
182
469
  </Link>
183
- <Link :href="prefix"
184
- class="text-[13px] text-neutral-500 transition-colors hover:text-white">
470
+ <Link
471
+ :href="prefix"
472
+ class="text-[13px] text-[var(--esc-panel-text-muted)] transition-colors hover:text-[var(--esc-panel-text)]"
473
+ >
185
474
  Back to App
186
475
  </Link>
187
- <div class="ml-1 h-5 w-px bg-white/[0.08]"></div>
476
+ <div class="ml-1 h-5 w-px bg-[var(--esc-panel-active)]"></div>
188
477
  <div class="flex items-center gap-2">
189
- <div class="flex h-7 w-7 items-center justify-center rounded-md bg-white/[0.08] text-[10px] font-semibold text-neutral-400">
478
+ <div
479
+ class="flex h-7 w-7 items-center justify-center rounded-md bg-[var(--esc-panel-active)] text-[10px] font-semibold text-[var(--esc-panel-text-tertiary)]"
480
+ >
190
481
  {{ userInitial }}
191
482
  </div>
192
- <span class="text-[13px] text-neutral-400">{{ userName }}</span>
483
+ <span class="text-[13px] text-[var(--esc-panel-text-tertiary)]">{{ userName }}</span>
193
484
  </div>
194
485
  </div>
195
486
  </div>
@@ -199,6 +490,30 @@ function isActive(href) {
199
490
  <main class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
200
491
  <slot />
201
492
  </main>
493
+
494
+ <!-- Powered by -->
495
+ <footer v-if="showPoweredBy" class="border-t border-[var(--esc-panel-border)] py-3 text-center">
496
+ <a
497
+ href="https://escalated.dev"
498
+ target="_blank"
499
+ rel="noopener noreferrer"
500
+ class="inline-flex items-center gap-1.5 text-[11px] text-[var(--esc-panel-text-muted)] opacity-60 transition-opacity hover:opacity-100"
501
+ >
502
+ <img
503
+ :src="
504
+ isDark
505
+ ? 'https://escalated.dev/brand/logo-icon-dark.svg'
506
+ : 'https://escalated.dev/brand/logo-icon-white.svg'
507
+ "
508
+ alt=""
509
+ class="h-3.5 w-3.5"
510
+ />
511
+ Powered by Escalated
512
+ </a>
513
+ </footer>
514
+
515
+ <!-- Active Chats Panel (agent layout only, when chat enabled) -->
516
+ <ActiveChatsPanel v-if="chatEnabled && agentId" :chats="activeChats" :agent-id="agentId" />
202
517
  </div>
203
518
 
204
519
  <!-- MODE 3: Customer pages — use host app layout if provided -->
@@ -208,8 +523,19 @@ function isActive(href) {
208
523
  <h2 class="text-xl font-semibold leading-tight text-gray-800">{{ title }}</h2>
209
524
  <nav class="flex items-center gap-4 text-sm">
210
525
  <Link :href="prefix" class="text-gray-600 hover:text-gray-900">My Tickets</Link>
211
- <Link v-if="isAgent" :href="`${prefix}/agent`" class="text-gray-600 hover:text-gray-900">Agent Panel</Link>
212
- <Link v-if="isAdmin" :href="`${prefix}/admin/reports`" class="text-gray-600 hover:text-gray-900">Admin</Link>
526
+ <Link
527
+ v-if="kbEnabled && (kbPublic || page.props.auth?.user)"
528
+ :href="`${prefix}/kb`"
529
+ class="text-gray-600 hover:text-gray-900"
530
+ >
531
+ Knowledge Base
532
+ </Link>
533
+ <Link v-if="isAgent" :href="`${prefix}/agent`" class="text-gray-600 hover:text-gray-900">
534
+ Agent Panel
535
+ </Link>
536
+ <Link v-if="isAdmin" :href="`${prefix}/admin/reports`" class="text-gray-600 hover:text-gray-900">
537
+ Admin
538
+ </Link>
213
539
  </nav>
214
540
  </div>
215
541
  </template>
@@ -228,8 +554,23 @@ function isActive(href) {
228
554
  <span class="text-lg font-bold text-gray-900">{{ title }}</span>
229
555
  <div class="flex items-center gap-4 text-sm">
230
556
  <Link :href="prefix" class="text-gray-600 hover:text-gray-900">My Tickets</Link>
231
- <Link v-if="isAgent" :href="`${prefix}/agent`" class="text-gray-600 hover:text-gray-900">Agent Panel</Link>
232
- <Link v-if="isAdmin" :href="`${prefix}/admin/reports`" class="text-gray-600 hover:text-gray-900">Admin</Link>
557
+ <Link
558
+ v-if="kbEnabled && (kbPublic || page.props.auth?.user)"
559
+ :href="`${prefix}/kb`"
560
+ class="text-gray-600 hover:text-gray-900"
561
+ >
562
+ Knowledge Base
563
+ </Link>
564
+ <Link v-if="isAgent" :href="`${prefix}/agent`" class="text-gray-600 hover:text-gray-900">
565
+ Agent Panel
566
+ </Link>
567
+ <Link
568
+ v-if="isAdmin"
569
+ :href="`${prefix}/admin/reports`"
570
+ class="text-gray-600 hover:text-gray-900"
571
+ >
572
+ Admin
573
+ </Link>
233
574
  </div>
234
575
  </div>
235
576
  </div>
@@ -240,5 +581,18 @@ function isActive(href) {
240
581
  <slot />
241
582
  </div>
242
583
  </main>
584
+
585
+ <!-- Powered by -->
586
+ <footer v-if="showPoweredBy" class="border-t border-gray-200 py-3 text-center">
587
+ <a
588
+ href="https://escalated.dev"
589
+ target="_blank"
590
+ rel="noopener noreferrer"
591
+ class="inline-flex items-center gap-1.5 text-[11px] text-gray-400 transition-colors hover:text-gray-600"
592
+ >
593
+ <img src="https://escalated.dev/brand/logo-icon-white.svg" alt="Escalated logo" class="h-3.5 w-3.5" />
594
+ Powered by Escalated
595
+ </a>
596
+ </footer>
243
597
  </div>
244
598
  </template>