@escalated-dev/escalated 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +94 -2
  2. package/package.json +31 -2
  3. package/src/components/ActivityTimeline.stories.js +55 -0
  4. package/src/components/ActivityTimeline.vue +24 -7
  5. package/src/components/AdminDashboard.stories.js +1095 -0
  6. package/src/components/AgentLoadIndicator.stories.js +37 -0
  7. package/src/components/AgentLoadIndicator.vue +49 -0
  8. package/src/components/AgentPerformanceCard.vue +44 -0
  9. package/src/components/ArticleEditor.vue +82 -0
  10. package/src/components/AssigneeSelect.vue +19 -5
  11. package/src/components/AttachmentList.vue +53 -37
  12. package/src/components/AuditLogEntry.vue +95 -0
  13. package/src/components/BulkActionBar.vue +99 -30
  14. package/src/components/CategoryTree.vue +89 -0
  15. package/src/components/ChartWidget.vue +46 -0
  16. package/src/components/CollisionWarning.stories.js +30 -0
  17. package/src/components/CollisionWarning.vue +68 -0
  18. package/src/components/ConditionalFieldRules.vue +135 -0
  19. package/src/components/ContextPanel.vue +66 -0
  20. package/src/components/ContextPanelSection.vue +65 -0
  21. package/src/components/CustomFieldRenderer.vue +159 -0
  22. package/src/components/EscalatedLayout.vue +400 -69
  23. package/src/components/FileDropzone.vue +26 -10
  24. package/src/components/FollowButton.stories.js +30 -0
  25. package/src/components/FollowButton.vue +53 -28
  26. package/src/components/KeyboardShortcutHelp.vue +129 -39
  27. package/src/components/KnowledgePanel.vue +122 -0
  28. package/src/components/KpiCard.stories.js +32 -0
  29. package/src/components/KpiCard.vue +88 -0
  30. package/src/components/MacroDropdown.vue +95 -35
  31. package/src/components/PermissionMatrix.vue +107 -0
  32. package/src/components/PinnedNotes.vue +70 -31
  33. package/src/components/PresenceIndicator.vue +93 -27
  34. package/src/components/PriorityBadge.stories.js +33 -0
  35. package/src/components/PriorityBadge.vue +9 -2
  36. package/src/components/QuickFilters.vue +65 -49
  37. package/src/components/ReplyComposer.vue +144 -30
  38. package/src/components/ReplyThread.vue +72 -20
  39. package/src/components/SatisfactionRating.vue +98 -46
  40. package/src/components/ScheduleEditor.vue +104 -0
  41. package/src/components/SideConversation.vue +165 -0
  42. package/src/components/SideConversationList.vue +147 -0
  43. package/src/components/SkillTagManager.vue +79 -0
  44. package/src/components/SlaComplianceChart.vue +53 -0
  45. package/src/components/SlaTimer.stories.js +35 -0
  46. package/src/components/SlaTimer.vue +17 -4
  47. package/src/components/StatsCard.stories.js +24 -0
  48. package/src/components/StatsCard.vue +58 -46
  49. package/src/components/StatusBadge.stories.js +53 -0
  50. package/src/components/StatusBadge.vue +36 -4
  51. package/src/components/TagSelect.vue +43 -12
  52. package/src/components/TicketFilters.vue +225 -69
  53. package/src/components/TicketLinkPanel.vue +193 -0
  54. package/src/components/TicketList.vue +594 -193
  55. package/src/components/TicketMergeDialog.vue +157 -0
  56. package/src/components/TicketSidebar.vue +167 -99
  57. package/src/components/TicketTypeSelector.vue +21 -0
  58. package/src/components/TotpInput.stories.js +9 -0
  59. package/src/components/TotpInput.vue +81 -0
  60. package/src/components/TwoFactorSetup.vue +113 -0
  61. package/src/composables/useDebouncedSearch.js +38 -0
  62. package/src/composables/usePluginExtensions.js +95 -3
  63. package/src/composables/usePluginHooks.js +65 -0
  64. package/src/defineEscalatedPlugin.js +34 -8
  65. package/src/index.js +72 -35
  66. package/src/locales/en.json +25 -0
  67. package/src/pages/Admin/ApiTokens/Index.vue +231 -67
  68. package/src/pages/Admin/AuditLog/Index.vue +154 -0
  69. package/src/pages/Admin/Automations/Form.vue +184 -0
  70. package/src/pages/Admin/Automations/Index.vue +129 -0
  71. package/src/pages/Admin/BusinessHours/Form.vue +172 -0
  72. package/src/pages/Admin/BusinessHours/Index.vue +113 -0
  73. package/src/pages/Admin/CannedResponses/Index.vue +127 -70
  74. package/src/pages/Admin/Capacity/Index.vue +134 -0
  75. package/src/pages/Admin/CustomFields/Form.vue +195 -0
  76. package/src/pages/Admin/CustomFields/Index.vue +134 -0
  77. package/src/pages/Admin/CustomObjects/Form.vue +202 -0
  78. package/src/pages/Admin/CustomObjects/Index.vue +127 -0
  79. package/src/pages/Admin/CustomObjects/Records.vue +228 -0
  80. package/src/pages/Admin/Departments/Form.vue +75 -51
  81. package/src/pages/Admin/Departments/Index.vue +109 -57
  82. package/src/pages/Admin/EscalationRules/Form.vue +157 -96
  83. package/src/pages/Admin/EscalationRules/Index.vue +178 -57
  84. package/src/pages/Admin/Import/Connect.vue +96 -0
  85. package/src/pages/Admin/Import/Index.vue +102 -0
  86. package/src/pages/Admin/Import/Mapping.vue +110 -0
  87. package/src/pages/Admin/Import/Progress.vue +153 -0
  88. package/src/pages/Admin/Import/Review.vue +76 -0
  89. package/src/pages/Admin/KnowledgeBase/Articles/Form.vue +142 -0
  90. package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +195 -0
  91. package/src/pages/Admin/KnowledgeBase/Categories/Index.vue +193 -0
  92. package/src/pages/Admin/Macros/Index.vue +250 -89
  93. package/src/pages/Admin/Plugins/Index.vue +205 -75
  94. package/src/pages/Admin/Reports/AgentMetrics.vue +115 -0
  95. package/src/pages/Admin/Reports/CsatReport.vue +127 -0
  96. package/src/pages/Admin/Reports/Dashboard.vue +211 -0
  97. package/src/pages/Admin/Reports/SlaReport.vue +166 -0
  98. package/src/pages/Admin/Reports.vue +139 -34
  99. package/src/pages/Admin/Roles/Form.vue +71 -0
  100. package/src/pages/Admin/Roles/Index.vue +122 -0
  101. package/src/pages/Admin/Settings/CsatSettings.vue +165 -0
  102. package/src/pages/Admin/Settings/DataRetention.vue +182 -0
  103. package/src/pages/Admin/Settings/EmailSettings.vue +243 -0
  104. package/src/pages/Admin/Settings/Sandbox.vue +135 -0
  105. package/src/pages/Admin/Settings/SsoSettings.vue +218 -0
  106. package/src/pages/Admin/Settings/TwoFactor.vue +110 -0
  107. package/src/pages/Admin/Settings.vue +468 -260
  108. package/src/pages/Admin/Skills/Form.vue +53 -0
  109. package/src/pages/Admin/Skills/Index.vue +94 -0
  110. package/src/pages/Admin/SlaPolicies/Form.vue +118 -75
  111. package/src/pages/Admin/SlaPolicies/Index.vue +120 -59
  112. package/src/pages/Admin/Statuses/Form.vue +114 -0
  113. package/src/pages/Admin/Statuses/Index.vue +153 -0
  114. package/src/pages/Admin/Tags/Index.vue +191 -105
  115. package/src/pages/Admin/Tickets/Index.vue +91 -39
  116. package/src/pages/Admin/Tickets/Show.vue +101 -37
  117. package/src/pages/Admin/Webhooks/DeliveryLog.vue +120 -0
  118. package/src/pages/Admin/Webhooks/Form.vue +110 -0
  119. package/src/pages/Admin/Webhooks/Index.vue +136 -0
  120. package/src/pages/Agent/Dashboard.vue +311 -186
  121. package/src/pages/Agent/TicketIndex.vue +88 -38
  122. package/src/pages/Agent/TicketShow.vue +258 -51
  123. package/src/pages/Customer/Create.vue +36 -9
  124. package/src/pages/Customer/Index.vue +31 -24
  125. package/src/pages/Customer/KnowledgeBase/Article.vue +127 -0
  126. package/src/pages/Customer/KnowledgeBase/Index.vue +151 -0
  127. package/src/plugin.js +86 -1
  128. package/src/utils/constants.js +30 -0
  129. package/src/utils/formatting.js +35 -0
  130. package/src/pages/Admin/Plugins.vue +0 -129
@@ -9,6 +9,7 @@ const props = defineProps({
9
9
 
10
10
  const page = usePage();
11
11
  const hostLayout = inject('escalated-layout', null);
12
+ const panelConfig = inject('escalated-panel', { appName: 'Escalated', logo: null, mode: 'dark' });
12
13
 
13
14
  const isAgent = computed(() => page.props.escalated?.is_agent);
14
15
  const isAdmin = computed(() => page.props.escalated?.is_admin);
@@ -20,7 +21,9 @@ const prefix = computed(() => {
20
21
  const currentUrl = computed(() => page.url);
21
22
  const isAdminSection = computed(() => currentUrl.value?.includes('/admin'));
22
23
  const isAgentSection = computed(() => currentUrl.value?.includes('/agent'));
23
- const isDark = computed(() => isAdminSection.value || isAgentSection.value);
24
+ const isPanel = computed(() => isAdminSection.value || isAgentSection.value);
25
+ const isDark = computed(() => isPanel.value && panelConfig.mode !== 'light');
26
+ const showPoweredBy = computed(() => page.props.escalated?.show_powered_by !== false);
24
27
 
25
28
  provide('esc-dark', isDark);
26
29
 
@@ -29,21 +32,148 @@ const { menuItems: pluginMenuItems } = usePluginExtensions();
29
32
  const adminLinks = computed(() => {
30
33
  const p = prefix.value;
31
34
  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 },
35
+ {
36
+ href: `${p}/admin/reports`,
37
+ label: 'Reports',
38
+ 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',
39
+ position: 10,
40
+ },
41
+ {
42
+ href: `${p}/admin/tickets`,
43
+ label: 'Tickets',
44
+ 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',
45
+ position: 20,
46
+ },
47
+ {
48
+ href: `${p}/admin/departments`,
49
+ label: 'Departments',
50
+ 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',
51
+ position: 30,
52
+ },
53
+ {
54
+ href: `${p}/admin/sla-policies`,
55
+ label: 'SLA Policies',
56
+ icon: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z',
57
+ position: 40,
58
+ },
59
+ {
60
+ href: `${p}/admin/knowledge-base/articles`,
61
+ label: 'Knowledge Base',
62
+ 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',
63
+ position: 45,
64
+ },
65
+ {
66
+ href: `${p}/admin/escalation-rules`,
67
+ label: 'Escalation Rules',
68
+ icon: 'M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12',
69
+ position: 50,
70
+ },
71
+ {
72
+ href: `${p}/admin/automations`,
73
+ label: 'Automations',
74
+ icon: 'M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182',
75
+ position: 52,
76
+ },
77
+ {
78
+ href: `${p}/admin/webhooks`,
79
+ label: 'Webhooks',
80
+ 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',
81
+ position: 54,
82
+ },
83
+ {
84
+ href: `${p}/admin/tags`,
85
+ label: 'Tags',
86
+ 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',
87
+ position: 60,
88
+ },
89
+ {
90
+ href: `${p}/admin/custom-fields`,
91
+ label: 'Custom Fields',
92
+ 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',
93
+ position: 62,
94
+ },
95
+ {
96
+ href: `${p}/admin/statuses`,
97
+ label: 'Statuses',
98
+ icon: 'M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
99
+ position: 64,
100
+ },
101
+ {
102
+ href: `${p}/admin/canned-responses`,
103
+ label: 'Canned Responses',
104
+ 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',
105
+ position: 70,
106
+ },
107
+ {
108
+ href: `${p}/admin/macros`,
109
+ label: 'Macros',
110
+ icon: 'M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z',
111
+ position: 80,
112
+ },
113
+ {
114
+ href: `${p}/admin/skills`,
115
+ label: 'Skills',
116
+ 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',
117
+ position: 82,
118
+ },
119
+ {
120
+ href: `${p}/admin/roles`,
121
+ label: 'Roles',
122
+ 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',
123
+ position: 84,
124
+ },
125
+ {
126
+ href: `${p}/admin/capacity`,
127
+ label: 'Capacity',
128
+ icon: 'M3 7.5L7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5',
129
+ position: 85,
130
+ },
131
+ {
132
+ href: `${p}/admin/business-hours`,
133
+ label: 'Business Hours',
134
+ 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',
135
+ position: 86,
136
+ },
137
+ {
138
+ href: `${p}/admin/audit-log`,
139
+ label: 'Audit Log',
140
+ 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',
141
+ position: 88,
142
+ },
143
+ {
144
+ href: `${p}/admin/api-tokens`,
145
+ label: 'API Tokens',
146
+ 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',
147
+ position: 90,
148
+ },
149
+ {
150
+ href: `${p}/admin/custom-objects`,
151
+ label: 'Custom Objects',
152
+ 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',
153
+ position: 92,
154
+ },
155
+ {
156
+ href: `${p}/admin/plugins`,
157
+ label: 'Plugins',
158
+ 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',
159
+ position: 95,
160
+ },
161
+ {
162
+ href: `${p}/admin/import`,
163
+ label: 'Import',
164
+ 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',
165
+ position: 97,
166
+ },
167
+ {
168
+ href: `${p}/admin/settings`,
169
+ label: 'Settings',
170
+ 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',
171
+ position: 100,
172
+ },
43
173
  ];
44
174
 
45
175
  // Merge core admin links with plugin-provided menu items
46
- const pluginLinks = pluginMenuItems.value.map(item => ({
176
+ const pluginLinks = pluginMenuItems.value.map((item) => ({
47
177
  href: item.route,
48
178
  label: item.label,
49
179
  icon: item.icon,
@@ -56,8 +186,16 @@ const adminLinks = computed(() => {
56
186
  });
57
187
 
58
188
  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' },
189
+ {
190
+ href: `${prefix.value}/agent`,
191
+ label: 'Dashboard',
192
+ 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',
193
+ },
194
+ {
195
+ href: `${prefix.value}/agent/tickets`,
196
+ label: 'Tickets',
197
+ 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',
198
+ },
61
199
  ]);
62
200
 
63
201
  const userName = computed(() => page.props.auth?.user?.name || 'User');
@@ -71,102 +209,242 @@ function isActive(href) {
71
209
 
72
210
  <template>
73
211
  <!-- MODE 1: Admin — dark sidebar layout -->
74
- <div v-if="isAdminSection" class="flex min-h-screen bg-black" style="color-scheme: dark">
212
+ <div
213
+ v-if="isAdminSection"
214
+ class="flex min-h-screen bg-[var(--esc-panel-bg)]"
215
+ :style="{ colorScheme: panelConfig.mode === 'light' ? 'light' : 'dark' }"
216
+ >
75
217
  <!-- 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">
218
+ <aside
219
+ 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)]"
220
+ >
77
221
  <!-- Logo -->
78
222
  <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>
223
+ <div class="flex h-9 w-9 items-center justify-center rounded-lg bg-[var(--esc-panel-border-input)]">
224
+ <img
225
+ v-if="typeof panelConfig.logo === 'string'"
226
+ :src="panelConfig.logo"
227
+ class="h-5 w-5"
228
+ alt="Escalated logo"
229
+ />
230
+ <component :is="panelConfig.logo" v-else-if="panelConfig.logo" class="h-5 w-5" />
231
+ <svg
232
+ v-else
233
+ class="h-5 w-5"
234
+ viewBox="0 0 24 24"
235
+ fill="none"
236
+ stroke-width="2.5"
237
+ stroke-linecap="round"
238
+ stroke-linejoin="round"
239
+ >
240
+ <defs>
241
+ <linearGradient id="esc-rainbow-admin" x1="0%" y1="0%" x2="100%" y2="100%">
242
+ <stop offset="0%" stop-color="#f97316" />
243
+ <stop offset="30%" stop-color="#eab308" />
244
+ <stop offset="50%" stop-color="#22c55e" />
245
+ <stop offset="70%" stop-color="#3b82f6" />
246
+ <stop offset="100%" stop-color="#8b5cf6" />
247
+ </linearGradient>
248
+ </defs>
249
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)">
250
+ <polyline points="17 11 12 6 7 11" stroke="url(#esc-rainbow-admin)" />
251
+ <polyline points="17 18 12 13 7 18" stroke="url(#esc-rainbow-admin)" />
252
+ </g>
83
253
  </svg>
84
254
  </div>
85
255
  <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>
256
+ <span class="text-sm font-bold text-[var(--esc-panel-text)] tracking-wide">{{
257
+ panelConfig.appName
258
+ }}</span>
259
+ <span
260
+ 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)]"
261
+ >ADMIN</span
262
+ >
88
263
  </div>
89
264
  </div>
90
265
 
91
266
  <!-- 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">
267
+ <nav role="navigation" aria-label="Admin navigation" class="mt-1 flex-1 space-y-0.5 overflow-y-auto px-3">
268
+ <Link
269
+ v-for="link in adminLinks"
270
+ :key="link.href"
271
+ :href="link.href"
272
+ :aria-current="isActive(link.href) ? 'page' : undefined"
273
+ :class="[
274
+ 'group flex items-center gap-3 rounded-lg px-3 py-2 text-[13px] font-medium transition-all',
275
+ isActive(link.href)
276
+ ? 'bg-[var(--esc-panel-active)] text-[var(--esc-panel-text)]'
277
+ : 'text-[var(--esc-panel-text-muted)] hover:bg-[var(--esc-panel-hover)] hover:text-[var(--esc-panel-text-secondary)]',
278
+ ]"
279
+ >
280
+ <svg
281
+ :class="[
282
+ 'h-[18px] w-[18px] shrink-0',
283
+ isActive(link.href)
284
+ ? 'text-[var(--esc-panel-text)]'
285
+ : 'text-[var(--esc-panel-text-muted)] group-hover:text-[var(--esc-panel-text-tertiary)]',
286
+ ]"
287
+ fill="none"
288
+ stroke="currentColor"
289
+ stroke-width="1.5"
290
+ viewBox="0 0 24 24"
291
+ >
100
292
  <path stroke-linecap="round" stroke-linejoin="round" :d="link.icon" />
101
293
  </svg>
102
294
  {{ 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">
295
+ <span
296
+ v-if="link.badge"
297
+ 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)]"
298
+ >
105
299
  {{ link.badge }}
106
300
  </span>
107
301
  </Link>
108
302
  </nav>
109
303
 
110
304
  <!-- 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>
305
+ <div class="border-t border-[var(--esc-panel-border)] p-3">
306
+ <Link
307
+ v-if="isAgent"
308
+ :href="`${prefix}/agent`"
309
+ 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)]"
310
+ >
311
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
312
+ <path
313
+ stroke-linecap="round"
314
+ stroke-linejoin="round"
315
+ d="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3"
316
+ />
317
+ </svg>
115
318
  Agent Panel
116
319
  </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>
320
+ <Link
321
+ :href="prefix"
322
+ 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)]"
323
+ >
324
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
325
+ <path
326
+ stroke-linecap="round"
327
+ stroke-linejoin="round"
328
+ 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"
329
+ />
330
+ </svg>
120
331
  Back to App
121
332
  </Link>
122
333
 
123
334
  <!-- 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">
335
+ <div class="mt-2 flex items-center gap-3 rounded-lg bg-[var(--esc-panel-hover)] px-3 py-2.5">
336
+ <div
337
+ 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)]"
338
+ >
126
339
  {{ userInitial }}
127
340
  </div>
128
- <span class="text-sm text-neutral-400">{{ userName }}</span>
341
+ <span class="text-sm text-[var(--esc-panel-text-tertiary)]">{{ userName }}</span>
129
342
  </div>
130
343
  </div>
344
+
345
+ <!-- Powered by -->
346
+ <div v-if="showPoweredBy" class="px-4 pb-3">
347
+ <a
348
+ href="https://escalated.dev"
349
+ target="_blank"
350
+ rel="noopener noreferrer"
351
+ 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"
352
+ >
353
+ <img
354
+ :src="
355
+ isDark
356
+ ? 'https://escalated.dev/brand/logo-icon-dark.svg'
357
+ : 'https://escalated.dev/brand/logo-icon-white.svg'
358
+ "
359
+ alt="Escalated logo"
360
+ class="h-3.5 w-3.5"
361
+ />
362
+ Powered by Escalated
363
+ </a>
364
+ </div>
131
365
  </aside>
132
366
 
133
367
  <!-- Main content -->
134
368
  <div class="flex flex-1 flex-col pl-64">
135
369
  <!-- 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>
370
+ <header
371
+ role="banner"
372
+ 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"
373
+ >
374
+ <h1 class="text-sm font-semibold text-[var(--esc-panel-text)]">{{ title }}</h1>
138
375
  </header>
139
376
 
140
377
  <!-- Page content -->
141
- <main class="flex-1 p-6">
378
+ <main role="main" class="flex-1 p-6">
142
379
  <slot />
143
380
  </main>
144
381
  </div>
145
382
  </div>
146
383
 
147
384
  <!-- MODE 2: Agent — dark top-nav layout -->
148
- <div v-else-if="isAgentSection" class="min-h-screen bg-black" style="color-scheme: dark">
385
+ <div
386
+ v-else-if="isAgentSection"
387
+ class="min-h-screen bg-[var(--esc-panel-bg)]"
388
+ :style="{ colorScheme: panelConfig.mode === 'light' ? 'light' : 'dark' }"
389
+ >
149
390
  <!-- Top nav -->
150
- <nav class="sticky top-0 z-30 border-b border-white/[0.06] bg-neutral-950/95 backdrop-blur-xl">
391
+ <nav
392
+ class="sticky top-0 z-30 border-b border-[var(--esc-panel-border)] bg-[var(--esc-panel-sidebar-bg)] backdrop-blur-xl"
393
+ >
151
394
  <div class="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
152
395
  <!-- Left: branding -->
153
396
  <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>
397
+ <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-[var(--esc-panel-border-input)]">
398
+ <img
399
+ v-if="typeof panelConfig.logo === 'string'"
400
+ :src="panelConfig.logo"
401
+ class="h-4 w-4"
402
+ alt="Escalated logo"
403
+ />
404
+ <component :is="panelConfig.logo" v-else-if="panelConfig.logo" class="h-4 w-4" />
405
+ <svg
406
+ v-else
407
+ class="h-4 w-4"
408
+ viewBox="0 0 24 24"
409
+ fill="none"
410
+ stroke-width="2.5"
411
+ stroke-linecap="round"
412
+ stroke-linejoin="round"
413
+ >
414
+ <defs>
415
+ <linearGradient id="esc-rainbow-agent" x1="0%" y1="0%" x2="100%" y2="100%">
416
+ <stop offset="0%" stop-color="#f97316" />
417
+ <stop offset="30%" stop-color="#eab308" />
418
+ <stop offset="50%" stop-color="#22c55e" />
419
+ <stop offset="70%" stop-color="#3b82f6" />
420
+ <stop offset="100%" stop-color="#8b5cf6" />
421
+ </linearGradient>
422
+ </defs>
423
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)">
424
+ <polyline points="17 11 12 6 7 11" stroke="url(#esc-rainbow-agent)" />
425
+ <polyline points="17 18 12 13 7 18" stroke="url(#esc-rainbow-agent)" />
426
+ </g>
158
427
  </svg>
159
428
  </div>
160
- <span class="text-sm font-bold text-white tracking-wide">Escalated</span>
429
+ <span class="text-sm font-bold text-[var(--esc-panel-text)] tracking-wide">{{
430
+ panelConfig.appName
431
+ }}</span>
161
432
  </div>
162
433
 
163
434
  <!-- Center: nav links -->
164
435
  <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']">
436
+ <Link
437
+ v-for="link in agentLinks"
438
+ :key="link.href"
439
+ :href="link.href"
440
+ :aria-current="isActive(link.href) ? 'page' : undefined"
441
+ :class="[
442
+ 'flex items-center gap-2 rounded-lg px-3.5 py-1.5 text-[13px] font-medium transition-all',
443
+ isActive(link.href)
444
+ ? 'bg-[var(--esc-panel-active)] text-[var(--esc-panel-text)]'
445
+ : 'text-[var(--esc-panel-text-muted)] hover:bg-[var(--esc-panel-hover)] hover:text-[var(--esc-panel-text-secondary)]',
446
+ ]"
447
+ >
170
448
  <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
171
449
  <path stroke-linecap="round" stroke-linejoin="round" :d="link.icon" />
172
450
  </svg>
@@ -176,20 +454,27 @@ function isActive(href) {
176
454
 
177
455
  <!-- Right: user + links -->
178
456
  <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">
457
+ <Link
458
+ v-if="isAdmin"
459
+ :href="`${prefix}/admin/reports`"
460
+ class="text-[13px] text-[var(--esc-panel-text-muted)] transition-colors hover:text-[var(--esc-panel-text)]"
461
+ >
181
462
  Admin
182
463
  </Link>
183
- <Link :href="prefix"
184
- class="text-[13px] text-neutral-500 transition-colors hover:text-white">
464
+ <Link
465
+ :href="prefix"
466
+ class="text-[13px] text-[var(--esc-panel-text-muted)] transition-colors hover:text-[var(--esc-panel-text)]"
467
+ >
185
468
  Back to App
186
469
  </Link>
187
- <div class="ml-1 h-5 w-px bg-white/[0.08]"></div>
470
+ <div class="ml-1 h-5 w-px bg-[var(--esc-panel-active)]"></div>
188
471
  <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">
472
+ <div
473
+ 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)]"
474
+ >
190
475
  {{ userInitial }}
191
476
  </div>
192
- <span class="text-[13px] text-neutral-400">{{ userName }}</span>
477
+ <span class="text-[13px] text-[var(--esc-panel-text-tertiary)]">{{ userName }}</span>
193
478
  </div>
194
479
  </div>
195
480
  </div>
@@ -199,6 +484,27 @@ function isActive(href) {
199
484
  <main class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
200
485
  <slot />
201
486
  </main>
487
+
488
+ <!-- Powered by -->
489
+ <footer v-if="showPoweredBy" class="border-t border-[var(--esc-panel-border)] py-3 text-center">
490
+ <a
491
+ href="https://escalated.dev"
492
+ target="_blank"
493
+ rel="noopener noreferrer"
494
+ class="inline-flex items-center gap-1.5 text-[11px] text-[var(--esc-panel-text-muted)] opacity-60 transition-opacity hover:opacity-100"
495
+ >
496
+ <img
497
+ :src="
498
+ isDark
499
+ ? 'https://escalated.dev/brand/logo-icon-dark.svg'
500
+ : 'https://escalated.dev/brand/logo-icon-white.svg'
501
+ "
502
+ alt=""
503
+ class="h-3.5 w-3.5"
504
+ />
505
+ Powered by Escalated
506
+ </a>
507
+ </footer>
202
508
  </div>
203
509
 
204
510
  <!-- MODE 3: Customer pages — use host app layout if provided -->
@@ -208,8 +514,12 @@ function isActive(href) {
208
514
  <h2 class="text-xl font-semibold leading-tight text-gray-800">{{ title }}</h2>
209
515
  <nav class="flex items-center gap-4 text-sm">
210
516
  <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>
517
+ <Link v-if="isAgent" :href="`${prefix}/agent`" class="text-gray-600 hover:text-gray-900">
518
+ Agent Panel
519
+ </Link>
520
+ <Link v-if="isAdmin" :href="`${prefix}/admin/reports`" class="text-gray-600 hover:text-gray-900">
521
+ Admin
522
+ </Link>
213
523
  </nav>
214
524
  </div>
215
525
  </template>
@@ -228,8 +538,16 @@ function isActive(href) {
228
538
  <span class="text-lg font-bold text-gray-900">{{ title }}</span>
229
539
  <div class="flex items-center gap-4 text-sm">
230
540
  <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>
541
+ <Link v-if="isAgent" :href="`${prefix}/agent`" class="text-gray-600 hover:text-gray-900">
542
+ Agent Panel
543
+ </Link>
544
+ <Link
545
+ v-if="isAdmin"
546
+ :href="`${prefix}/admin/reports`"
547
+ class="text-gray-600 hover:text-gray-900"
548
+ >
549
+ Admin
550
+ </Link>
233
551
  </div>
234
552
  </div>
235
553
  </div>
@@ -240,5 +558,18 @@ function isActive(href) {
240
558
  <slot />
241
559
  </div>
242
560
  </main>
561
+
562
+ <!-- Powered by -->
563
+ <footer v-if="showPoweredBy" class="border-t border-gray-200 py-3 text-center">
564
+ <a
565
+ href="https://escalated.dev"
566
+ target="_blank"
567
+ rel="noopener noreferrer"
568
+ class="inline-flex items-center gap-1.5 text-[11px] text-gray-400 transition-colors hover:text-gray-600"
569
+ >
570
+ <img src="https://escalated.dev/brand/logo-icon-white.svg" alt="Escalated logo" class="h-3.5 w-3.5" />
571
+ Powered by Escalated
572
+ </a>
573
+ </footer>
243
574
  </div>
244
575
  </template>