@escalated-dev/escalated 0.7.0 → 0.8.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 (181) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +36 -1
  3. package/package.json +16 -7
  4. package/src/components/ActiveChatsPanel.vue +246 -0
  5. package/src/components/ActivityTimeline.stories.js +55 -55
  6. package/src/components/ActivityTimeline.vue +83 -83
  7. package/src/components/AdminDashboard.stories.js +1423 -1095
  8. package/src/components/AgentLoadIndicator.stories.js +37 -37
  9. package/src/components/AgentLoadIndicator.vue +49 -49
  10. package/src/components/AgentPerformanceCard.vue +44 -44
  11. package/src/components/ArticleEditor.vue +82 -82
  12. package/src/components/AssigneeSelect.vue +37 -37
  13. package/src/components/AttachmentList.vue +13 -1
  14. package/src/components/AuditLogEntry.vue +95 -95
  15. package/src/components/BulkActionBar.vue +166 -166
  16. package/src/components/CategoryTree.vue +89 -89
  17. package/src/components/ChartWidget.vue +46 -46
  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 -30
  24. package/src/components/CollisionWarning.vue +68 -68
  25. package/src/components/ConditionalFieldRules.vue +135 -135
  26. package/src/components/ContextPanel.vue +66 -66
  27. package/src/components/ContextPanelSection.vue +65 -65
  28. package/src/components/CustomFieldRenderer.vue +159 -159
  29. package/src/components/EscalatedLayout.vue +35 -6
  30. package/src/components/FileDropzone.vue +57 -57
  31. package/src/components/FollowButton.stories.js +30 -30
  32. package/src/components/FollowButton.vue +88 -88
  33. package/src/components/KeyboardShortcutHelp.vue +213 -213
  34. package/src/components/KnowledgePanel.vue +122 -122
  35. package/src/components/KpiCard.stories.js +32 -32
  36. package/src/components/KpiCard.vue +88 -88
  37. package/src/components/MacroDropdown.vue +150 -150
  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 -107
  43. package/src/components/PinnedNotes.vue +129 -129
  44. package/src/components/PluginSlot.vue +68 -62
  45. package/src/components/PresenceIndicator.vue +46 -2
  46. package/src/components/PriorityBadge.stories.js +33 -33
  47. package/src/components/PriorityBadge.vue +46 -46
  48. package/src/components/ReplyComposer.vue +81 -18
  49. package/src/components/ReplyThread.vue +40 -2
  50. package/src/components/ReportChart.vue +395 -0
  51. package/src/components/ReportExportButton.vue +108 -0
  52. package/src/components/SatisfactionRating.vue +180 -180
  53. package/src/components/SaveViewDialog.vue +172 -0
  54. package/src/components/SavedViewSidebar.vue +208 -0
  55. package/src/components/ScheduleEditor.vue +104 -104
  56. package/src/components/SideConversation.vue +165 -165
  57. package/src/components/SideConversationList.vue +147 -147
  58. package/src/components/SkillTagManager.vue +83 -79
  59. package/src/components/SlaComplianceChart.vue +46 -53
  60. package/src/components/SlaTimer.stories.js +35 -35
  61. package/src/components/SlaTimer.vue +62 -62
  62. package/src/components/SnoozeButton.vue +247 -0
  63. package/src/components/StatsCard.stories.js +24 -24
  64. package/src/components/StatsCard.vue +58 -58
  65. package/src/components/StatusBadge.stories.js +53 -53
  66. package/src/components/StatusBadge.vue +78 -78
  67. package/src/components/TagSelect.vue +84 -84
  68. package/src/components/TicketFilters.vue +270 -225
  69. package/src/components/TicketLinkPanel.vue +193 -193
  70. package/src/components/TicketList.vue +655 -594
  71. package/src/components/TicketMergeDialog.vue +157 -157
  72. package/src/components/TicketSplitDialog.vue +98 -0
  73. package/src/components/TotpInput.stories.js +9 -9
  74. package/src/components/TotpInput.vue +81 -81
  75. package/src/components/TwoFactorSetup.vue +113 -113
  76. package/src/components/VariableTokenInput.vue +111 -0
  77. package/src/components/WorkflowActionConfig.vue +287 -0
  78. package/src/components/WorkflowActionList.vue +350 -0
  79. package/src/components/WorkflowConditionBuilder.vue +265 -0
  80. package/src/components/WorkflowTriggerSelector.vue +125 -0
  81. package/src/composables/useChat.js +187 -0
  82. package/src/composables/useI18n.js +30 -8
  83. package/src/composables/useKeyboardShortcuts.js +46 -46
  84. package/src/composables/useMentions.js +134 -0
  85. package/src/composables/usePluginExtensions.js +159 -159
  86. package/src/composables/usePluginHooks.js +65 -65
  87. package/src/composables/useRealtime.js +182 -0
  88. package/src/defineEscalatedPlugin.js +76 -76
  89. package/src/index.js +84 -72
  90. package/src/locales/README.md +13 -0
  91. package/src/locales/ar.json +727 -0
  92. package/src/locales/de.json +678 -430
  93. package/src/locales/en.json +717 -455
  94. package/src/locales/es.json +679 -430
  95. package/src/locales/fr.json +680 -431
  96. package/src/locales/index.js +12 -2
  97. package/src/locales/it.json +727 -0
  98. package/src/locales/ja.json +727 -0
  99. package/src/locales/ko.json +727 -0
  100. package/src/locales/nl.json +727 -0
  101. package/src/locales/pl.json +727 -0
  102. package/src/locales/pt-BR.json +727 -0
  103. package/src/locales/ru.json +727 -0
  104. package/src/locales/tr.json +727 -0
  105. package/src/locales/zh-CN.json +727 -0
  106. package/src/pages/Admin/ApiTokens/Index.vue +381 -381
  107. package/src/pages/Admin/AuditLog/Index.vue +154 -154
  108. package/src/pages/Admin/BusinessHours/Form.vue +172 -172
  109. package/src/pages/Admin/BusinessHours/Index.vue +113 -113
  110. package/src/pages/Admin/CannedResponses/Index.vue +127 -127
  111. package/src/pages/Admin/Capacity/Index.vue +134 -134
  112. package/src/pages/Admin/CustomFields/Form.vue +195 -195
  113. package/src/pages/Admin/CustomFields/Index.vue +134 -134
  114. package/src/pages/Admin/CustomObjects/Form.vue +202 -202
  115. package/src/pages/Admin/CustomObjects/Index.vue +127 -127
  116. package/src/pages/Admin/CustomObjects/Records.vue +228 -228
  117. package/src/pages/Admin/Departments/Form.vue +75 -75
  118. package/src/pages/Admin/Departments/Index.vue +110 -109
  119. package/src/pages/Admin/EscalationRules/Form.vue +157 -157
  120. package/src/pages/Admin/EscalationRules/Index.vue +178 -178
  121. package/src/pages/Admin/Import/Connect.vue +0 -1
  122. package/src/pages/Admin/Import/Index.vue +1 -1
  123. package/src/pages/Admin/Import/Progress.vue +1 -1
  124. package/src/pages/Admin/KnowledgeBase/Articles/Form.vue +142 -142
  125. package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +195 -195
  126. package/src/pages/Admin/KnowledgeBase/Categories/Index.vue +193 -193
  127. package/src/pages/Admin/Macros/Index.vue +448 -448
  128. package/src/pages/Admin/Plugins/Index.vue +327 -327
  129. package/src/pages/Admin/Reports/AgentDetail.vue +132 -0
  130. package/src/pages/Admin/Reports/AgentMetrics.vue +115 -115
  131. package/src/pages/Admin/Reports/AgentRanking.vue +246 -0
  132. package/src/pages/Admin/Reports/Cohorts.vue +180 -0
  133. package/src/pages/Admin/Reports/Comparison.vue +141 -0
  134. package/src/pages/Admin/Reports/CsatReport.vue +127 -127
  135. package/src/pages/Admin/Reports/Dashboard.vue +36 -21
  136. package/src/pages/Admin/Reports/ResolutionTimes.vue +164 -0
  137. package/src/pages/Admin/Reports/ResponseTimes.vue +164 -0
  138. package/src/pages/Admin/Reports/SlaReport.vue +162 -166
  139. package/src/pages/Admin/Reports/SlaTrends.vue +183 -0
  140. package/src/pages/Admin/Reports.vue +206 -206
  141. package/src/pages/Admin/Roles/Form.vue +71 -71
  142. package/src/pages/Admin/Roles/Index.vue +122 -122
  143. package/src/pages/Admin/Settings/CsatSettings.vue +165 -165
  144. package/src/pages/Admin/Settings/DataRetention.vue +182 -182
  145. package/src/pages/Admin/Settings/EmailSettings.vue +243 -243
  146. package/src/pages/Admin/Settings/Sandbox.vue +135 -135
  147. package/src/pages/Admin/Settings/SsoSettings.vue +218 -218
  148. package/src/pages/Admin/Settings/TwoFactor.vue +110 -110
  149. package/src/pages/Admin/Settings.vue +1008 -468
  150. package/src/pages/Admin/Skills/Form.vue +193 -53
  151. package/src/pages/Admin/Skills/Index.vue +105 -94
  152. package/src/pages/Admin/SlaPolicies/Form.vue +118 -118
  153. package/src/pages/Admin/SlaPolicies/Index.vue +121 -120
  154. package/src/pages/Admin/Statuses/Form.vue +114 -114
  155. package/src/pages/Admin/Statuses/Index.vue +153 -153
  156. package/src/pages/Admin/Tags/Index.vue +191 -191
  157. package/src/pages/Admin/Tickets/Index.vue +126 -91
  158. package/src/pages/Admin/Tickets/Show.vue +29 -0
  159. package/src/pages/Admin/Users/Index.vue +145 -0
  160. package/src/pages/Admin/Webhooks/DeliveryLog.vue +120 -120
  161. package/src/pages/Admin/Webhooks/Form.vue +110 -110
  162. package/src/pages/Admin/Webhooks/Index.vue +136 -136
  163. package/src/pages/Admin/Workflows/Builder.vue +207 -0
  164. package/src/pages/Admin/Workflows/Index.vue +235 -0
  165. package/src/pages/Admin/Workflows/Logs.vue +286 -0
  166. package/src/pages/Agent/TicketIndex.vue +112 -88
  167. package/src/pages/Agent/TicketShow.vue +192 -60
  168. package/src/pages/Customer/Create.vue +1 -1
  169. package/src/pages/Customer/Index.vue +26 -1
  170. package/src/pages/Customer/KnowledgeBase/Article.vue +4 -2
  171. package/src/pages/Customer/KnowledgeBase/Index.vue +151 -151
  172. package/src/pages/Customer/Show.vue +69 -63
  173. package/src/pages/Guest/Create.vue +40 -15
  174. package/src/pages/Guest/Show.vue +124 -93
  175. package/src/plugin.js +26 -1
  176. package/src/utils/mentionHighlight.js +24 -0
  177. package/src/utils/sanitizeHtml.js +78 -64
  178. package/src/widget/EscalatedWidget.vue +804 -0
  179. package/src/widget/index.js +254 -0
  180. package/src/pages/Admin/Automations/Form.vue +0 -184
  181. package/src/pages/Admin/Automations/Index.vue +0 -129
@@ -1,1095 +1,1423 @@
1
- import { computed } from 'vue';
2
- import StatsCard from './StatsCard.vue';
3
- import KpiCard from './KpiCard.vue';
4
- import StatusBadge from './StatusBadge.vue';
5
- import PriorityBadge from './PriorityBadge.vue';
6
- import AgentLoadIndicator from './AgentLoadIndicator.vue';
7
-
8
- export default {
9
- title: 'Pages/AdminDashboard',
10
- };
11
-
12
- const sidebarLinks = [
13
- {
14
- label: 'Reports',
15
- 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',
16
- active: true,
17
- },
18
- {
19
- label: 'Tickets',
20
- 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',
21
- badge: '24',
22
- },
23
- {
24
- label: 'Departments',
25
- 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',
26
- },
27
- { label: 'SLA Policies', icon: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z' },
28
- {
29
- label: 'Knowledge Base',
30
- 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',
31
- },
32
- { label: 'Escalation Rules', icon: 'M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12' },
33
- {
34
- label: 'Automations',
35
- 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',
36
- },
37
- {
38
- label: 'Tags',
39
- 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',
40
- },
41
- {
42
- label: 'Plugins',
43
- 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',
44
- },
45
- {
46
- label: 'Settings',
47
- 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',
48
- },
49
- ];
50
-
51
- const tickets = [
52
- {
53
- ref: 'ESC-1042',
54
- subject: 'Unable to export CSV reports from dashboard',
55
- requester: 'Sarah Chen',
56
- email: 'sarah@acme.co',
57
- status: 'open',
58
- priority: 'high',
59
- ago: '12m ago',
60
- author: 'Agent Mike',
61
- sla: 'green',
62
- },
63
- {
64
- ref: 'ESC-1041',
65
- subject: 'SSO login redirect loop on Firefox',
66
- requester: 'James Wilson',
67
- email: 'james@corp.io',
68
- status: 'pending',
69
- priority: 'urgent',
70
- ago: '34m ago',
71
- author: 'Sarah Chen',
72
- sla: 'amber',
73
- },
74
- {
75
- ref: 'ESC-1040',
76
- subject: 'Custom field values not persisting after save',
77
- requester: 'Maria Garcia',
78
- email: 'maria@startup.dev',
79
- status: 'open',
80
- priority: 'medium',
81
- ago: '1h ago',
82
- author: 'Agent Lisa',
83
- sla: 'green',
84
- },
85
- {
86
- ref: 'ESC-1039',
87
- subject: 'Webhook notifications delayed by 15+ minutes',
88
- requester: 'Tom Baker',
89
- email: 'tom@enterprise.com',
90
- status: 'open',
91
- priority: 'high',
92
- ago: '2h ago',
93
- author: 'Tom Baker',
94
- sla: 'red',
95
- },
96
- {
97
- ref: 'ESC-1038',
98
- subject: 'Knowledge base search returning no results',
99
- requester: 'Priya Patel',
100
- email: 'priya@example.com',
101
- status: 'solved',
102
- priority: 'medium',
103
- ago: '3h ago',
104
- author: 'Agent Mike',
105
- sla: '',
106
- },
107
- {
108
- ref: 'ESC-1037',
109
- subject: 'Feature request: Dark mode for customer portal',
110
- requester: 'Alex Kim',
111
- email: 'alex@design.co',
112
- status: 'open',
113
- priority: 'low',
114
- ago: '5h ago',
115
- author: 'Alex Kim',
116
- sla: '',
117
- },
118
- ];
119
-
120
- /**
121
- * Full admin dashboard with sidebar, top bar, stats cards, and ticket list.
122
- * Used for README hero screenshot.
123
- */
124
- export const FullDashboard = {
125
- render: () => ({
126
- components: { StatsCard, KpiCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
127
- data: () => ({ sidebarLinks, tickets }),
128
- template: `
129
- <div style="display:flex; min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden;">
130
- <!-- Sidebar -->
131
- <aside style="width:240px; background:#0a0a0a; border-right:1px solid rgba(255,255,255,0.06); display:flex; flex-direction:column; flex-shrink:0;">
132
- <!-- Logo -->
133
- <div style="height:56px; display:flex; align-items:center; gap:10px; padding:0 16px;">
134
- <div style="width:32px; height:32px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
135
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
136
- <defs><linearGradient id="esc-rb" 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>
137
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb)"/></g>
138
- </svg>
139
- </div>
140
- <div>
141
- <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
142
- <span style="margin-left:6px; background:rgba(255,255,255,0.08); padding:2px 6px; border-radius:4px; font-size:10px; font-weight:600; color:#737373;">ADMIN</span>
143
- </div>
144
- </div>
145
-
146
- <!-- Nav links -->
147
- <nav style="flex:1; padding:4px 10px; display:flex; flex-direction:column; gap:1px; overflow-y:auto;">
148
- <a v-for="link in sidebarLinks" :key="link.label"
149
- :style="{
150
- display: 'flex', alignItems: 'center', gap: '10px',
151
- padding: '7px 10px', borderRadius: '8px',
152
- fontSize: '13px', fontWeight: '500', textDecoration: 'none',
153
- color: link.active ? '#fff' : '#737373',
154
- background: link.active ? 'rgba(255,255,255,0.08)' : 'transparent',
155
- }">
156
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" :stroke="link.active ? '#fff' : '#737373'" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" :d="link.icon"/></svg>
157
- {{ link.label }}
158
- <span v-if="link.badge" style="margin-left:auto; background:rgba(6,182,212,0.2); color:#06b6d4; font-size:10px; font-weight:600; padding:1px 6px; border-radius:10px;">{{ link.badge }}</span>
159
- </a>
160
- </nav>
161
-
162
- <!-- Bottom user -->
163
- <div style="border-top:1px solid rgba(255,255,255,0.06); padding:10px;">
164
- <div style="display:flex; align-items:center; gap:10px; background:rgba(255,255,255,0.03); border-radius:8px; padding:8px 10px;">
165
- <div style="width:28px; height:28px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#a3a3a3;">J</div>
166
- <span style="font-size:13px; color:#a3a3a3;">Jane Admin</span>
167
- </div>
168
- </div>
169
- </aside>
170
-
171
- <!-- Main -->
172
- <div style="flex:1; display:flex; flex-direction:column; min-width:0;">
173
- <!-- Top bar -->
174
- <header style="height:48px; display:flex; align-items:center; border-bottom:1px solid rgba(255,255,255,0.06); background:rgba(0,0,0,0.8); padding:0 20px; backdrop-filter:blur(12px); flex-shrink:0;">
175
- <span style="font-size:13px; font-weight:600;">Reports</span>
176
- </header>
177
-
178
- <!-- Content -->
179
- <main style="flex:1; padding:20px; overflow-y:auto;">
180
- <!-- KPI row -->
181
- <div class="grid grid-cols-3 gap-4" style="margin-bottom:20px;">
182
- <KpiCard label="Total Tickets" :value="1284" :trend="5" icon="M2.25 13.5h3.86a2.25 2.25 0 012.012 1.244l.256.512a2.25 2.25 0 002.013 1.244h3.218a2.25 2.25 0 002.013-1.244l.256-.512a2.25 2.25 0 012.013-1.244h3.859" />
183
- <KpiCard label="Avg Response Time" value="1h 42m" :trend="-18" icon="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
184
- <KpiCard label="CSAT Score" value="94%" :trend="3" icon="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
185
- </div>
186
-
187
- <!-- Stats row -->
188
- <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
189
- <StatsCard label="Open Tickets" :value="142" trend="+8% this week" color="blue" />
190
- <StatsCard label="Resolved Today" :value="38" trend="+12% vs yesterday" color="green" />
191
- <StatsCard label="Avg Response" value="2h 14m" trend="-18% improvement" color="cyan" />
192
- <StatsCard label="SLA Breaches" :value="3" trend="-25% vs last week" color="red" />
193
- </div>
194
-
195
- <!-- Recent tickets table -->
196
- <div style="border-radius:12px; border:1px solid rgba(255,255,255,0.06); background:rgba(23,23,23,0.6); overflow:hidden;">
197
- <div style="padding:12px 16px; border-bottom:1px solid rgba(255,255,255,0.06); display:flex; align-items:center; justify-content:space-between;">
198
- <span style="font-size:13px; font-weight:600;">Recent Tickets</span>
199
- <span style="font-size:12px; color:#737373;">Last 24 hours</span>
200
- </div>
201
- <table style="width:100%; border-collapse:collapse;">
202
- <thead>
203
- <tr style="background:rgba(255,255,255,0.02);">
204
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Reference</th>
205
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Subject</th>
206
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Requester</th>
207
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Status</th>
208
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Priority</th>
209
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Last Reply</th>
210
- </tr>
211
- </thead>
212
- <tbody>
213
- <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid rgba(255,255,255,0.04);" class="hover:bg-white/[0.03]">
214
- <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;">
215
- <div style="display:flex; align-items:center; gap:6px;">
216
- <span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span>
217
- <span style="color:#22d3ee;">{{ t.ref }}</span>
218
- </div>
219
- </td>
220
- <td style="padding:10px 14px; font-size:13px; color:#d4d4d4; max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
221
- <td style="padding:10px 14px;">
222
- <div style="font-size:13px; color:#a3a3a3;">{{ t.requester }}</div>
223
- <div style="font-size:11px; color:#525252;">{{ t.email }}</div>
224
- </td>
225
- <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
226
- <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
227
- <td style="padding:10px 14px; white-space:nowrap;">
228
- <div style="font-size:13px; color:#a3a3a3;">{{ t.ago }}</div>
229
- <div style="font-size:11px; color:#525252;">{{ t.author }}</div>
230
- </td>
231
- </tr>
232
- </tbody>
233
- </table>
234
- </div>
235
- </main>
236
- </div>
237
- </div>
238
- `,
239
- }),
240
- };
241
-
242
- /**
243
- * Light mode admin dashboard — same layout, light theme.
244
- * Used for README hero screenshot.
245
- */
246
- export const FullDashboardLight = {
247
- render: () => ({
248
- components: { StatsCard, KpiCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
249
- data: () => ({ sidebarLinks, tickets }),
250
- provide: { 'esc-dark': computed(() => false) },
251
- template: `
252
- <div style="display:flex; min-height:720px; background:#f9fafb; color:#111827; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden; border: 1px solid #e5e7eb;">
253
- <aside style="width:240px; background:#ffffff; border-right:1px solid #e5e7eb; display:flex; flex-direction:column; flex-shrink:0;">
254
- <div style="height:56px; display:flex; align-items:center; gap:10px; padding:0 16px;">
255
- <div style="width:32px; height:32px; border-radius:8px; background:#f3f4f6; display:flex; align-items:center; justify-content:center;">
256
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
257
- <defs><linearGradient id="esc-rb-l" 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>
258
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb-l)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb-l)"/></g>
259
- </svg>
260
- </div>
261
- <div>
262
- <span style="font-size:13px; font-weight:700; letter-spacing:0.02em; color:#111827;">Escalated</span>
263
- <span style="margin-left:6px; background:#eff6ff; padding:2px 6px; border-radius:4px; font-size:10px; font-weight:600; color:#3b82f6;">ADMIN</span>
264
- </div>
265
- </div>
266
- <nav style="flex:1; padding:4px 10px; display:flex; flex-direction:column; gap:1px; overflow-y:auto;">
267
- <a v-for="link in sidebarLinks" :key="link.label" :style="{ display:'flex', alignItems:'center', gap:'10px', padding:'7px 10px', borderRadius:'8px', fontSize:'13px', fontWeight:'500', textDecoration:'none', color: link.active ? '#111827' : '#6b7280', background: link.active ? '#eff6ff' : 'transparent' }">
268
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" :stroke="link.active ? '#3b82f6' : '#9ca3af'" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" :d="link.icon"/></svg>
269
- {{ link.label }}
270
- <span v-if="link.badge" style="margin-left:auto; background:#dbeafe; color:#2563eb; font-size:10px; font-weight:600; padding:1px 6px; border-radius:10px;">{{ link.badge }}</span>
271
- </a>
272
- </nav>
273
- <div style="border-top:1px solid #e5e7eb; padding:10px;">
274
- <div style="display:flex; align-items:center; gap:10px; background:#f9fafb; border-radius:8px; padding:8px 10px;">
275
- <div style="width:28px; height:28px; border-radius:6px; background:#e5e7eb; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#374151;">J</div>
276
- <span style="font-size:13px; color:#374151;">Jane Admin</span>
277
- </div>
278
- </div>
279
- </aside>
280
- <div style="flex:1; display:flex; flex-direction:column; min-width:0;">
281
- <header style="height:48px; display:flex; align-items:center; border-bottom:1px solid #e5e7eb; background:rgba(255,255,255,0.95); padding:0 20px; backdrop-filter:blur(12px); flex-shrink:0;">
282
- <span style="font-size:13px; font-weight:600; color:#111827;">Reports</span>
283
- </header>
284
- <main style="flex:1; padding:20px; overflow-y:auto;">
285
- <div class="grid grid-cols-3 gap-4" style="margin-bottom:20px;">
286
- <KpiCard label="Total Tickets" :value="1284" :trend="5" icon="M2.25 13.5h3.86a2.25 2.25 0 012.012 1.244l.256.512a2.25 2.25 0 002.013 1.244h3.218a2.25 2.25 0 002.013-1.244l.256-.512a2.25 2.25 0 012.013-1.244h3.859" />
287
- <KpiCard label="Avg Response Time" value="1h 42m" :trend="-18" icon="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
288
- <KpiCard label="CSAT Score" value="94%" :trend="3" icon="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
289
- </div>
290
- <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
291
- <StatsCard label="Open Tickets" :value="142" trend="+8% this week" color="blue" />
292
- <StatsCard label="Resolved Today" :value="38" trend="+12% vs yesterday" color="green" />
293
- <StatsCard label="Avg Response" value="2h 14m" trend="-18% improvement" color="cyan" />
294
- <StatsCard label="SLA Breaches" :value="3" trend="-25% vs last week" color="red" />
295
- </div>
296
- <div style="border-radius:12px; border:1px solid #e5e7eb; background:#ffffff; overflow:hidden;">
297
- <div style="padding:12px 16px; border-bottom:1px solid #e5e7eb; display:flex; align-items:center; justify-content:space-between;">
298
- <span style="font-size:13px; font-weight:600; color:#111827;">Recent Tickets</span>
299
- <span style="font-size:12px; color:#9ca3af;">Last 24 hours</span>
300
- </div>
301
- <table style="width:100%; border-collapse:collapse;">
302
- <thead><tr style="background:#f9fafb;">
303
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Reference</th>
304
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Subject</th>
305
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Requester</th>
306
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Status</th>
307
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Priority</th>
308
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Last Reply</th>
309
- </tr></thead>
310
- <tbody>
311
- <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid #f3f4f6;">
312
- <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;"><div style="display:flex; align-items:center; gap:6px;"><span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span><span style="color:#2563eb;">{{ t.ref }}</span></div></td>
313
- <td style="padding:10px 14px; font-size:13px; color:#111827; max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
314
- <td style="padding:10px 14px;"><div style="font-size:13px; color:#374151;">{{ t.requester }}</div><div style="font-size:11px; color:#9ca3af;">{{ t.email }}</div></td>
315
- <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
316
- <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
317
- <td style="padding:10px 14px; white-space:nowrap;"><div style="font-size:13px; color:#374151;">{{ t.ago }}</div><div style="font-size:11px; color:#9ca3af;">{{ t.author }}</div></td>
318
- </tr>
319
- </tbody>
320
- </table>
321
- </div>
322
- </main>
323
- </div>
324
- </div>
325
- `,
326
- }),
327
- };
328
-
329
- /**
330
- * Agent panel view with top navigation bar, dashboard stats, and ticket list.
331
- * Used for README hero screenshot.
332
- */
333
- export const AgentPanel = {
334
- render: () => ({
335
- components: { StatsCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
336
- data: () => ({
337
- tickets: [
338
- {
339
- ref: 'ESC-1042',
340
- subject: 'Unable to export CSV reports from dashboard',
341
- requester: 'Sarah Chen',
342
- status: 'open',
343
- priority: 'high',
344
- ago: '12m ago',
345
- sla: 'green',
346
- },
347
- {
348
- ref: 'ESC-1041',
349
- subject: 'SSO login redirect loop on Firefox',
350
- requester: 'James Wilson',
351
- status: 'pending',
352
- priority: 'urgent',
353
- ago: '34m ago',
354
- sla: 'amber',
355
- },
356
- {
357
- ref: 'ESC-1040',
358
- subject: 'Custom field values not persisting after save',
359
- requester: 'Maria Garcia',
360
- status: 'open',
361
- priority: 'medium',
362
- ago: '1h ago',
363
- sla: 'green',
364
- },
365
- {
366
- ref: 'ESC-1039',
367
- subject: 'Webhook notifications delayed by 15+ minutes',
368
- requester: 'Tom Baker',
369
- status: 'open',
370
- priority: 'high',
371
- ago: '2h ago',
372
- sla: 'red',
373
- },
374
- {
375
- ref: 'ESC-1038',
376
- subject: 'Knowledge base search returning no results',
377
- requester: 'Priya Patel',
378
- status: 'solved',
379
- priority: 'medium',
380
- ago: '3h ago',
381
- sla: '',
382
- },
383
- ],
384
- }),
385
- template: `
386
- <div style="min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden;">
387
- <!-- Top nav -->
388
- <nav style="height:48px; border-bottom:1px solid rgba(255,255,255,0.06); background:#0a0a0a; display:flex; align-items:center; justify-content:space-between; padding:0 24px;">
389
- <div style="display:flex; align-items:center; gap:10px;">
390
- <div style="width:30px; height:30px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
391
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
392
- <defs><linearGradient id="esc-rb2" 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>
393
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb2)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb2)"/></g>
394
- </svg>
395
- </div>
396
- <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
397
- </div>
398
- <div style="display:flex; align-items:center; gap:4px;">
399
- <span style="background:rgba(255,255,255,0.08); padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Dashboard</span>
400
- <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#737373;">Tickets</span>
401
- </div>
402
- <div style="display:flex; align-items:center; gap:12px;">
403
- <span style="font-size:13px; color:#737373;">Admin</span>
404
- <div style="width:1px; height:16px; background:rgba(255,255,255,0.08);"></div>
405
- <div style="display:flex; align-items:center; gap:8px;">
406
- <div style="width:26px; height:26px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#a3a3a3;">M</div>
407
- <span style="font-size:13px; color:#a3a3a3;">Mike Agent</span>
408
- </div>
409
- </div>
410
- </nav>
411
-
412
- <!-- Content -->
413
- <main style="max-width:1100px; margin:0 auto; padding:20px 24px;">
414
- <!-- Stats -->
415
- <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
416
- <StatsCard label="My Open" :value="18" trend="+2 today" color="blue" />
417
- <StatsCard label="Unassigned" :value="7" trend="3 urgent" color="red" />
418
- <StatsCard label="Avg Response" value="1h 08m" trend="-22% this week" color="cyan" />
419
- <StatsCard label="Resolved Today" :value="12" trend="+4 vs yesterday" color="green" />
420
- </div>
421
-
422
- <!-- My tickets table -->
423
- <div style="border-radius:12px; border:1px solid rgba(255,255,255,0.06); background:rgba(23,23,23,0.6); overflow:hidden;">
424
- <div style="padding:12px 16px; border-bottom:1px solid rgba(255,255,255,0.06); display:flex; align-items:center; justify-content:space-between;">
425
- <span style="font-size:13px; font-weight:600;">My Tickets</span>
426
- <span style="font-size:12px; color:#737373;">Assigned to me</span>
427
- </div>
428
- <table style="width:100%; border-collapse:collapse;">
429
- <thead>
430
- <tr style="background:rgba(255,255,255,0.02);">
431
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Reference</th>
432
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Subject</th>
433
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Requester</th>
434
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Status</th>
435
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Priority</th>
436
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Updated</th>
437
- </tr>
438
- </thead>
439
- <tbody>
440
- <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid rgba(255,255,255,0.04);">
441
- <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;">
442
- <div style="display:flex; align-items:center; gap:6px;">
443
- <span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span>
444
- <span style="color:#22d3ee;">{{ t.ref }}</span>
445
- </div>
446
- </td>
447
- <td style="padding:10px 14px; font-size:13px; color:#d4d4d4; max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
448
- <td style="padding:10px 14px; font-size:13px; color:#a3a3a3;">{{ t.requester }}</td>
449
- <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
450
- <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
451
- <td style="padding:10px 14px; font-size:13px; color:#a3a3a3; white-space:nowrap;">{{ t.ago }}</td>
452
- </tr>
453
- </tbody>
454
- </table>
455
- </div>
456
- </main>
457
- </div>
458
- `,
459
- }),
460
- };
461
-
462
- /**
463
- * Ticket detail view — agent viewing a single ticket with conversation thread and sidebar.
464
- * Dark mode. Used for README hero screenshot.
465
- */
466
- export const TicketDetailView = {
467
- render: () => ({
468
- components: { StatusBadge, PriorityBadge },
469
- data: () => ({
470
- messages: [
471
- {
472
- id: 1,
473
- author: 'Sarah Chen',
474
- initial: 'S',
475
- role: 'customer',
476
- time: 'Mar 20, 2026 at 2:14 PM',
477
- text: "Hi, I'm trying to export our monthly CSV report from the dashboard but the download never starts. I've tried Chrome and Firefox, both on macOS. The button spinner appears but then nothing happens after about 30 seconds. This is blocking our end-of-quarter review.",
478
- },
479
- {
480
- id: 2,
481
- author: 'Mike Agent',
482
- initial: 'M',
483
- role: 'agent',
484
- time: 'Mar 20, 2026 at 2:32 PM',
485
- text: "Hi Sarah, thanks for reporting this. I can reproduce the issue on our end. It looks like exports with more than 10,000 rows are timing out. I've escalated this to our engineering team and we're working on a fix. In the meantime, could you try filtering to a smaller date range as a workaround?",
486
- },
487
- {
488
- id: 3,
489
- author: 'Sarah Chen',
490
- initial: 'S',
491
- role: 'customer',
492
- time: 'Mar 20, 2026 at 3:05 PM',
493
- text: 'Thanks Mike! Filtering to a single month did work as a workaround. That said, we really need the full quarter export for our finance team. Is there an ETA on the fix?',
494
- },
495
- {
496
- id: 4,
497
- author: 'Mike Agent',
498
- initial: 'M',
499
- role: 'agent',
500
- time: 'Mar 20, 2026 at 3:18 PM',
501
- text: "Great to hear the workaround helped! Engineering has identified the root cause — it's a memory limit on the export worker. The fix is in review and should ship by tomorrow morning. I'll update this ticket as soon as it's deployed.",
502
- },
503
- ],
504
- activeTab: 'reply',
505
- }),
506
- template: `
507
- <div style="min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden;">
508
- <!-- Top nav (same as AgentPanel) -->
509
- <nav style="height:48px; border-bottom:1px solid rgba(255,255,255,0.06); background:#0a0a0a; display:flex; align-items:center; justify-content:space-between; padding:0 24px; flex-shrink:0;">
510
- <div style="display:flex; align-items:center; gap:10px;">
511
- <div style="width:30px; height:30px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
512
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
513
- <defs><linearGradient id="esc-rb3" 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>
514
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb3)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb3)"/></g>
515
- </svg>
516
- </div>
517
- <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
518
- </div>
519
- <div style="display:flex; align-items:center; gap:4px;">
520
- <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#737373;">Dashboard</span>
521
- <span style="background:rgba(255,255,255,0.08); padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Tickets</span>
522
- </div>
523
- <div style="display:flex; align-items:center; gap:12px;">
524
- <span style="font-size:13px; color:#737373;">Admin</span>
525
- <div style="width:1px; height:16px; background:rgba(255,255,255,0.08);"></div>
526
- <div style="display:flex; align-items:center; gap:8px;">
527
- <div style="width:26px; height:26px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#a3a3a3;">M</div>
528
- <span style="font-size:13px; color:#a3a3a3;">Mike Agent</span>
529
- </div>
530
- </div>
531
- </nav>
532
-
533
- <!-- Main content area: left (thread) + right (sidebar) -->
534
- <div style="display:flex; flex:1; min-height:672px;">
535
- <!-- Left: ticket thread -->
536
- <div style="flex:7; display:flex; flex-direction:column; border-right:1px solid rgba(255,255,255,0.06);">
537
- <!-- Ticket header -->
538
- <div style="padding:16px 24px; border-bottom:1px solid rgba(255,255,255,0.06);">
539
- <div style="display:flex; align-items:center; gap:8px; margin-bottom:6px;">
540
- <span style="font-size:13px; color:#22d3ee; font-weight:500;">ESC-1042</span>
541
- <StatusBadge status="in_progress" />
542
- <PriorityBadge priority="high" />
543
- </div>
544
- <h1 style="font-size:16px; font-weight:600; margin:0; color:#f5f5f5;">Unable to export CSV reports from dashboard</h1>
545
- </div>
546
-
547
- <!-- Conversation thread -->
548
- <div style="flex:1; overflow-y:auto; padding:16px 24px; display:flex; flex-direction:column; gap:16px;">
549
- <div v-for="msg in messages" :key="msg.id" style="display:flex; gap:12px;">
550
- <!-- Avatar -->
551
- <div :style="{
552
- width: '32px', height: '32px', borderRadius: '8px', flexShrink: 0,
553
- display: 'flex', alignItems: 'center', justifyContent: 'center',
554
- fontSize: '12px', fontWeight: '600',
555
- background: msg.role === 'agent' ? 'rgba(59,130,246,0.15)' : 'rgba(255,255,255,0.08)',
556
- color: msg.role === 'agent' ? '#60a5fa' : '#a3a3a3',
557
- }">{{ msg.initial }}</div>
558
- <!-- Message body -->
559
- <div style="flex:1; min-width:0;">
560
- <div style="display:flex; align-items:center; gap:8px; margin-bottom:4px;">
561
- <span style="font-size:13px; font-weight:600;" :style="{ color: msg.role === 'agent' ? '#60a5fa' : '#e5e5e5' }">{{ msg.author }}</span>
562
- <span v-if="msg.role === 'agent'" style="font-size:10px; background:rgba(59,130,246,0.12); color:#60a5fa; padding:1px 6px; border-radius:4px; font-weight:500;">Agent</span>
563
- <span style="font-size:11px; color:#525252;">{{ msg.time }}</span>
564
- </div>
565
- <div style="font-size:13px; line-height:1.6; color:#d4d4d4; background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.04); border-radius:10px; padding:10px 14px;">
566
- {{ msg.text }}
567
- </div>
568
- </div>
569
- </div>
570
- </div>
571
-
572
- <!-- Reply composer -->
573
- <div style="border-top:1px solid rgba(255,255,255,0.06); padding:12px 24px;">
574
- <div style="display:flex; gap:0; margin-bottom:8px;">
575
- <button :style="{
576
- padding: '5px 14px', fontSize: '12px', fontWeight: '600', border: 'none', cursor: 'pointer', borderRadius: '6px 0 0 6px',
577
- background: activeTab === 'reply' ? 'rgba(59,130,246,0.15)' : 'rgba(255,255,255,0.05)',
578
- color: activeTab === 'reply' ? '#60a5fa' : '#737373',
579
- }" @click="activeTab = 'reply'">Reply</button>
580
- <button :style="{
581
- padding: '5px 14px', fontSize: '12px', fontWeight: '600', border: 'none', cursor: 'pointer', borderRadius: '0 6px 6px 0',
582
- background: activeTab === 'note' ? 'rgba(234,179,8,0.12)' : 'rgba(255,255,255,0.05)',
583
- color: activeTab === 'note' ? '#facc15' : '#737373',
584
- }" @click="activeTab = 'note'">Internal Note</button>
585
- </div>
586
- <div style="position:relative;">
587
- <textarea :placeholder="activeTab === 'reply' ? 'Type your reply...' : 'Add an internal note...'" style="width:100%; min-height:68px; background:rgba(255,255,255,0.04); border:1px solid rgba(255,255,255,0.08); border-radius:8px; padding:10px 14px; font-size:13px; color:#e5e5e5; resize:none; font-family:inherit; box-sizing:border-box;" />
588
- <div style="display:flex; justify-content:flex-end; margin-top:8px;">
589
- <span :style="{
590
- padding: '6px 16px', borderRadius: '8px', fontSize: '12px', fontWeight: '600', cursor: 'pointer',
591
- background: activeTab === 'reply' ? '#3b82f6' : '#ca8a04',
592
- color: '#fff',
593
- }">{{ activeTab === 'reply' ? 'Send Reply' : 'Add Note' }}</span>
594
- </div>
595
- </div>
596
- </div>
597
- </div>
598
-
599
- <!-- Right: ticket sidebar -->
600
- <div style="flex:3; padding:16px; overflow-y:auto; background:rgba(10,10,10,0.5);">
601
- <!-- Assignee -->
602
- <div style="margin-bottom:20px;">
603
- <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Assignee</div>
604
- <div style="display:flex; align-items:center; gap:8px;">
605
- <div style="width:28px; height:28px; border-radius:6px; background:rgba(59,130,246,0.15); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#60a5fa;">M</div>
606
- <div>
607
- <div style="font-size:13px; font-weight:500; color:#e5e5e5;">Mike Agent</div>
608
- <div style="font-size:11px; color:#525252;">Support Team</div>
609
- </div>
610
- </div>
611
- </div>
612
-
613
- <!-- Department -->
614
- <div style="margin-bottom:20px;">
615
- <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Department</div>
616
- <div style="font-size:13px; color:#a3a3a3;">Technical Support</div>
617
- </div>
618
-
619
- <!-- Tags -->
620
- <div style="margin-bottom:20px;">
621
- <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Tags</div>
622
- <div style="display:flex; flex-wrap:wrap; gap:4px;">
623
- <span style="background:rgba(255,255,255,0.06); color:#a3a3a3; font-size:11px; padding:2px 8px; border-radius:6px;">csv-export</span>
624
- <span style="background:rgba(255,255,255,0.06); color:#a3a3a3; font-size:11px; padding:2px 8px; border-radius:6px;">dashboard</span>
625
- <span style="background:rgba(255,255,255,0.06); color:#a3a3a3; font-size:11px; padding:2px 8px; border-radius:6px;">bug</span>
626
- </div>
627
- </div>
628
-
629
- <!-- SLA Timer -->
630
- <div style="margin-bottom:20px;">
631
- <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">SLA</div>
632
- <div style="border:1px solid rgba(16,185,129,0.2); background:rgba(16,185,129,0.08); border-radius:8px; padding:8px 12px;">
633
- <div style="font-size:12px; font-weight:500; color:#34d399;">Next Response Due</div>
634
- <div style="font-size:11px; color:#34d399;">3h 42m remaining</div>
635
- </div>
636
- </div>
637
-
638
- <!-- Requester Info -->
639
- <div style="margin-bottom:20px;">
640
- <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Requester</div>
641
- <div style="background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.06); border-radius:8px; padding:10px 12px;">
642
- <div style="display:flex; align-items:center; gap:8px; margin-bottom:8px;">
643
- <div style="width:28px; height:28px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#a3a3a3;">S</div>
644
- <div>
645
- <div style="font-size:13px; font-weight:500; color:#e5e5e5;">Sarah Chen</div>
646
- <div style="font-size:11px; color:#525252;">sarah@acme.co</div>
647
- </div>
648
- </div>
649
- <div style="display:flex; justify-content:space-between; font-size:11px; color:#525252; border-top:1px solid rgba(255,255,255,0.04); padding-top:8px;">
650
- <span>Org: Acme Corp</span>
651
- <span>12 tickets</span>
652
- </div>
653
- </div>
654
- </div>
655
-
656
- <!-- Created / Updated -->
657
- <div>
658
- <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Details</div>
659
- <div style="font-size:12px; color:#525252; display:flex; flex-direction:column; gap:4px;">
660
- <div style="display:flex; justify-content:space-between;"><span>Created</span><span style="color:#737373;">Mar 20, 2026</span></div>
661
- <div style="display:flex; justify-content:space-between;"><span>Updated</span><span style="color:#737373;">2 min ago</span></div>
662
- <div style="display:flex; justify-content:space-between;"><span>Channel</span><span style="color:#737373;">Email</span></div>
663
- </div>
664
- </div>
665
- </div>
666
- </div>
667
- </div>
668
- `,
669
- }),
670
- };
671
-
672
- export const AgentPanelLight = {
673
- render: () => ({
674
- components: { StatsCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
675
- provide: { 'esc-dark': computed(() => false) },
676
- data: () => ({
677
- tickets: [
678
- {
679
- ref: 'ESC-1042',
680
- subject: 'Unable to export CSV reports from dashboard',
681
- requester: 'Sarah Chen',
682
- status: 'open',
683
- priority: 'high',
684
- ago: '12m ago',
685
- sla: 'green',
686
- },
687
- {
688
- ref: 'ESC-1041',
689
- subject: 'SSO login redirect loop on Firefox',
690
- requester: 'James Wilson',
691
- status: 'pending',
692
- priority: 'urgent',
693
- ago: '34m ago',
694
- sla: 'amber',
695
- },
696
- {
697
- ref: 'ESC-1040',
698
- subject: 'Custom field values not persisting after save',
699
- requester: 'Maria Garcia',
700
- status: 'open',
701
- priority: 'medium',
702
- ago: '1h ago',
703
- sla: 'green',
704
- },
705
- {
706
- ref: 'ESC-1039',
707
- subject: 'Webhook notifications delayed by 15+ minutes',
708
- requester: 'Tom Baker',
709
- status: 'open',
710
- priority: 'high',
711
- ago: '2h ago',
712
- sla: 'red',
713
- },
714
- {
715
- ref: 'ESC-1038',
716
- subject: 'Knowledge base search returning no results',
717
- requester: 'Priya Patel',
718
- status: 'solved',
719
- priority: 'medium',
720
- ago: '3h ago',
721
- sla: '',
722
- },
723
- ],
724
- }),
725
- template: `
726
- <div style="min-height:720px; background:#f9fafb; color:#111827; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden; border: 1px solid #e5e7eb;">
727
- <!-- Top nav -->
728
- <nav style="height:48px; border-bottom:1px solid #e5e7eb; background:#ffffff; display:flex; align-items:center; justify-content:space-between; padding:0 24px;">
729
- <div style="display:flex; align-items:center; gap:10px;">
730
- <div style="width:30px; height:30px; border-radius:8px; background:#f3f4f6; display:flex; align-items:center; justify-content:center;">
731
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
732
- <defs><linearGradient id="esc-rb2l" 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>
733
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb2l)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb2l)"/></g>
734
- </svg>
735
- </div>
736
- <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
737
- </div>
738
- <div style="display:flex; align-items:center; gap:4px;">
739
- <span style="background:#eff6ff; padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Dashboard</span>
740
- <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#6b7280;">Tickets</span>
741
- </div>
742
- <div style="display:flex; align-items:center; gap:12px;">
743
- <span style="font-size:13px; color:#6b7280;">Admin</span>
744
- <div style="width:1px; height:16px; background:#eff6ff;"></div>
745
- <div style="display:flex; align-items:center; gap:8px;">
746
- <div style="width:26px; height:26px; border-radius:6px; background:#eff6ff; display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#374151;">M</div>
747
- <span style="font-size:13px; color:#374151;">Mike Agent</span>
748
- </div>
749
- </div>
750
- </nav>
751
-
752
- <!-- Content -->
753
- <main style="max-width:1100px; margin:0 auto; padding:20px 24px;">
754
- <!-- Stats -->
755
- <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
756
- <StatsCard label="My Open" :value="18" trend="+2 today" color="blue" />
757
- <StatsCard label="Unassigned" :value="7" trend="3 urgent" color="red" />
758
- <StatsCard label="Avg Response" value="1h 08m" trend="-22% this week" color="cyan" />
759
- <StatsCard label="Resolved Today" :value="12" trend="+4 vs yesterday" color="green" />
760
- </div>
761
-
762
- <!-- My tickets table -->
763
- <div style="border-radius:12px; border:1px solid #e5e7eb; background:#ffffff; overflow:hidden;">
764
- <div style="padding:12px 16px; border-bottom:1px solid #e5e7eb; display:flex; align-items:center; justify-content:space-between;">
765
- <span style="font-size:13px; font-weight:600;">My Tickets</span>
766
- <span style="font-size:12px; color:#6b7280;">Assigned to me</span>
767
- </div>
768
- <table style="width:100%; border-collapse:collapse;">
769
- <thead>
770
- <tr style="background:#f3f4f6;">
771
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Reference</th>
772
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Subject</th>
773
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Requester</th>
774
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Status</th>
775
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Priority</th>
776
- <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Updated</th>
777
- </tr>
778
- </thead>
779
- <tbody>
780
- <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid #f3f4f6;">
781
- <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;">
782
- <div style="display:flex; align-items:center; gap:6px;">
783
- <span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span>
784
- <span style="color:#2563eb;">{{ t.ref }}</span>
785
- </div>
786
- </td>
787
- <td style="padding:10px 14px; font-size:13px; color:#111827; max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
788
- <td style="padding:10px 14px; font-size:13px; color:#374151;">{{ t.requester }}</td>
789
- <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
790
- <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
791
- <td style="padding:10px 14px; font-size:13px; color:#374151; white-space:nowrap;">{{ t.ago }}</td>
792
- </tr>
793
- </tbody>
794
- </table>
795
- </div>
796
- </main>
797
- </div>
798
- `,
799
- }),
800
- };
801
-
802
- /**
803
- * Ticket list view — agent ticket queue with filters, search, table, and pagination.
804
- * Used for README hero screenshot.
805
- */
806
- export const TicketListView = {
807
- render: () => ({
808
- components: { StatusBadge, PriorityBadge },
809
- data: () => ({
810
- chips: [
811
- { label: 'My Tickets', active: true },
812
- { label: 'Unassigned', active: false },
813
- { label: 'Urgent+', active: false },
814
- { label: 'SLA Breaching', active: false },
815
- { label: 'Following', active: false },
816
- ],
817
- tickets: [
818
- {
819
- ref: 'ESC-1042',
820
- subject: 'Unable to export CSV reports from dashboard',
821
- requester: 'Sarah Chen',
822
- email: 'sarah@acme.co',
823
- status: 'open',
824
- priority: 'high',
825
- assignee: 'Mike Agent',
826
- ago: '12m ago',
827
- author: 'Agent Mike',
828
- sla: 'green',
829
- checked: true,
830
- },
831
- {
832
- ref: 'ESC-1041',
833
- subject: 'SSO login redirect loop on Firefox',
834
- requester: 'James Wilson',
835
- email: 'james@corp.io',
836
- status: 'pending',
837
- priority: 'urgent',
838
- assignee: 'Lisa Torres',
839
- ago: '34m ago',
840
- author: 'James Wilson',
841
- sla: 'amber',
842
- checked: true,
843
- },
844
- {
845
- ref: 'ESC-1040',
846
- subject: 'Custom field values not persisting after save',
847
- requester: 'Maria Garcia',
848
- email: 'maria@startup.dev',
849
- status: 'in_progress',
850
- priority: 'medium',
851
- assignee: 'Mike Agent',
852
- ago: '1h ago',
853
- author: 'Agent Lisa',
854
- sla: 'green',
855
- checked: false,
856
- },
857
- {
858
- ref: 'ESC-1039',
859
- subject: 'Webhook notifications delayed by 15+ minutes',
860
- requester: 'Tom Baker',
861
- email: 'tom@enterprise.com',
862
- status: 'escalated',
863
- priority: 'high',
864
- assignee: 'Mike Agent',
865
- ago: '2h ago',
866
- author: 'Tom Baker',
867
- sla: 'red',
868
- checked: false,
869
- },
870
- {
871
- ref: 'ESC-1038',
872
- subject: 'Knowledge base search returning no results',
873
- requester: 'Priya Patel',
874
- email: 'priya@example.com',
875
- status: 'solved',
876
- priority: 'medium',
877
- assignee: 'Lisa Torres',
878
- ago: '3h ago',
879
- author: 'Agent Mike',
880
- sla: '',
881
- checked: false,
882
- },
883
- {
884
- ref: 'ESC-1037',
885
- subject: 'Feature request: Dark mode for customer portal',
886
- requester: 'Alex Kim',
887
- email: 'alex@design.co',
888
- status: 'open',
889
- priority: 'low',
890
- assignee: 'Unassigned',
891
- ago: '5h ago',
892
- author: 'Alex Kim',
893
- sla: '',
894
- checked: false,
895
- },
896
- {
897
- ref: 'ESC-1036',
898
- subject: 'Mobile app crashes when uploading attachments',
899
- requester: 'David Liu',
900
- email: 'david@bigcorp.com',
901
- status: 'in_progress',
902
- priority: 'urgent',
903
- assignee: 'Mike Agent',
904
- ago: '6h ago',
905
- author: 'Agent Mike',
906
- sla: 'amber',
907
- checked: false,
908
- },
909
- {
910
- ref: 'ESC-1035',
911
- subject: 'Automation rule not triggering on status change',
912
- requester: 'Nina Petrova',
913
- email: 'nina@saas.io',
914
- status: 'pending',
915
- priority: 'medium',
916
- assignee: 'Lisa Torres',
917
- ago: '8h ago',
918
- author: 'Nina Petrova',
919
- sla: 'green',
920
- checked: false,
921
- },
922
- ],
923
- }),
924
- template: `
925
- <div style="min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden; display:flex; flex-direction:column;">
926
- <!-- Top nav (same as AgentPanel with Tickets active) -->
927
- <nav style="height:48px; border-bottom:1px solid rgba(255,255,255,0.06); background:#0a0a0a; display:flex; align-items:center; justify-content:space-between; padding:0 24px; flex-shrink:0;">
928
- <div style="display:flex; align-items:center; gap:10px;">
929
- <div style="width:30px; height:30px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
930
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
931
- <defs><linearGradient id="esc-rb4" 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>
932
- <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb4)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb4)"/></g>
933
- </svg>
934
- </div>
935
- <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
936
- </div>
937
- <div style="display:flex; align-items:center; gap:4px;">
938
- <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#737373;">Dashboard</span>
939
- <span style="background:rgba(255,255,255,0.08); padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Tickets</span>
940
- </div>
941
- <div style="display:flex; align-items:center; gap:12px;">
942
- <span style="font-size:13px; color:#737373;">Admin</span>
943
- <div style="width:1px; height:16px; background:rgba(255,255,255,0.08);"></div>
944
- <div style="display:flex; align-items:center; gap:8px;">
945
- <div style="width:26px; height:26px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#a3a3a3;">M</div>
946
- <span style="font-size:13px; color:#a3a3a3;">Mike Agent</span>
947
- </div>
948
- </div>
949
- </nav>
950
-
951
- <!-- Content area -->
952
- <div style="flex:1; display:flex; justify-content:center; padding:20px 24px; overflow-y:auto;">
953
- <div style="max-width:1100px; width:100%; display:flex; flex-direction:column; gap:16px;">
954
-
955
- <!-- Quick filter chips -->
956
- <div style="display:flex; gap:8px; flex-wrap:wrap;">
957
- <span v-for="chip in chips" :key="chip.label"
958
- :style="{
959
- padding: '4px 14px',
960
- borderRadius: '20px',
961
- fontSize: '12px',
962
- fontWeight: '500',
963
- cursor: 'pointer',
964
- border: chip.active ? '1px solid rgba(6,182,212,0.4)' : '1px solid rgba(255,255,255,0.08)',
965
- background: chip.active ? 'rgba(6,182,212,0.15)' : 'rgba(255,255,255,0.04)',
966
- color: chip.active ? '#22d3ee' : '#a3a3a3',
967
- }">
968
- {{ chip.label }}
969
- </span>
970
- </div>
971
-
972
- <!-- Search / filter bar -->
973
- <div style="display:flex; align-items:center; gap:10px; flex-wrap:wrap;">
974
- <div style="flex:1; min-width:200px; position:relative;">
975
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#737373" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="position:absolute; left:10px; top:50%; transform:translateY(-50%);"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
976
- <input type="text" placeholder="Search tickets..." style="width:100%; padding:7px 12px 7px 32px; border-radius:8px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#fff; font-size:13px; outline:none;" />
977
- </div>
978
- <select style="padding:7px 10px; border-radius:8px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:12px; outline:none; appearance:auto;">
979
- <option>Status</option>
980
- <option>Open</option>
981
- <option>Pending</option>
982
- <option>In Progress</option>
983
- <option>Solved</option>
984
- <option>Escalated</option>
985
- </select>
986
- <select style="padding:7px 10px; border-radius:8px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:12px; outline:none; appearance:auto;">
987
- <option>Priority</option>
988
- <option>Low</option>
989
- <option>Medium</option>
990
- <option>High</option>
991
- <option>Urgent</option>
992
- </select>
993
- <span style="font-size:12px; color:#06b6d4; cursor:pointer; font-weight:500; padding:4px 8px;">Advanced</span>
994
- </div>
995
-
996
- <!-- Ticket table -->
997
- <div style="border-radius:12px; border:1px solid rgba(255,255,255,0.06); background:rgba(23,23,23,0.6); overflow:hidden; flex:1;">
998
- <table style="width:100%; border-collapse:collapse; font-size:13px;">
999
- <thead>
1000
- <tr style="border-bottom:1px solid rgba(255,255,255,0.06);">
1001
- <th style="padding:10px 14px; text-align:left; width:28px;">
1002
- <div style="width:14px; height:14px; border:1px solid rgba(255,255,255,0.2); border-radius:3px;"></div>
1003
- </th>
1004
- <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Reference</th>
1005
- <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Subject</th>
1006
- <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Requester</th>
1007
- <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Status</th>
1008
- <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Priority</th>
1009
- <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Assignee</th>
1010
- <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Last Reply</th>
1011
- </tr>
1012
- </thead>
1013
- <tbody>
1014
- <tr v-for="t in tickets" :key="t.ref"
1015
- :style="{
1016
- borderBottom: '1px solid rgba(255,255,255,0.04)',
1017
- background: t.checked ? 'rgba(6,182,212,0.06)' : 'transparent',
1018
- }">
1019
- <td style="padding:10px 14px;">
1020
- <div :style="{
1021
- width: '14px', height: '14px', borderRadius: '3px',
1022
- border: t.checked ? '1px solid #06b6d4' : '1px solid rgba(255,255,255,0.15)',
1023
- background: t.checked ? '#06b6d4' : 'transparent',
1024
- display: 'flex', alignItems: 'center', justifyContent: 'center',
1025
- }">
1026
- <svg v-if="t.checked" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
1027
- </div>
1028
- </td>
1029
- <td style="padding:10px 8px; white-space:nowrap;">
1030
- <div style="display:flex; align-items:center; gap:6px;">
1031
- <span :style="{
1032
- width: '7px', height: '7px', borderRadius: '50%',
1033
- background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : t.sla === 'green' ? '#22c55e' : 'rgba(255,255,255,0.1)',
1034
- flexShrink: 0,
1035
- }"></span>
1036
- <span style="color:#22d3ee; font-weight:500;">{{ t.ref }}</span>
1037
- </div>
1038
- </td>
1039
- <td style="padding:10px 8px; max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#e5e5e5;">{{ t.subject }}</td>
1040
- <td style="padding:10px 8px; white-space:nowrap;">
1041
- <div style="display:flex; flex-direction:column;">
1042
- <span style="color:#d4d4d4; font-size:13px;">{{ t.requester }}</span>
1043
- <span style="color:#525252; font-size:11px;">{{ t.email }}</span>
1044
- </div>
1045
- </td>
1046
- <td style="padding:10px 8px;"><StatusBadge :status="t.status" /></td>
1047
- <td style="padding:10px 8px;"><PriorityBadge :priority="t.priority" /></td>
1048
- <td style="padding:10px 8px; white-space:nowrap;">
1049
- <span :style="{ color: t.assignee === 'Unassigned' ? '#525252' : '#a3a3a3', fontStyle: t.assignee === 'Unassigned' ? 'italic' : 'normal' }">{{ t.assignee }}</span>
1050
- </td>
1051
- <td style="padding:10px 8px; white-space:nowrap;">
1052
- <div style="display:flex; flex-direction:column;">
1053
- <span style="color:#a3a3a3; font-size:12px;">{{ t.ago }}</span>
1054
- <span style="color:#525252; font-size:11px;">{{ t.author }}</span>
1055
- </div>
1056
- </td>
1057
- </tr>
1058
- </tbody>
1059
- </table>
1060
- </div>
1061
-
1062
- <!-- Pagination -->
1063
- <div style="display:flex; align-items:center; justify-content:space-between;">
1064
- <span style="font-size:12px; color:#737373;">Showing 1\u20138 of 142 tickets</span>
1065
- <div style="display:flex; gap:6px;">
1066
- <button style="padding:5px 14px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#525252; font-size:12px; cursor:default;">Previous</button>
1067
- <button style="padding:5px 14px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#e5e5e5; font-size:12px; cursor:pointer;">Next</button>
1068
- </div>
1069
- </div>
1070
-
1071
- <!-- Bulk action bar -->
1072
- <div style="display:flex; align-items:center; gap:12px; padding:8px 14px; border-radius:10px; background:rgba(6,182,212,0.08); border:1px solid rgba(6,182,212,0.15);">
1073
- <span style="font-size:12px; color:#22d3ee; font-weight:500;">2 tickets selected</span>
1074
- <div style="width:1px; height:14px; background:rgba(255,255,255,0.08);"></div>
1075
- <select style="padding:4px 8px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:11px; outline:none; appearance:auto;">
1076
- <option>Set Status...</option>
1077
- <option>Open</option>
1078
- <option>Pending</option>
1079
- <option>Solved</option>
1080
- </select>
1081
- <select style="padding:4px 8px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:11px; outline:none; appearance:auto;">
1082
- <option>Set Priority...</option>
1083
- <option>Low</option>
1084
- <option>Medium</option>
1085
- <option>High</option>
1086
- <option>Urgent</option>
1087
- </select>
1088
- </div>
1089
-
1090
- </div>
1091
- </div>
1092
- </div>
1093
- `,
1094
- }),
1095
- };
1
+ import { computed } from 'vue';
2
+ import StatsCard from './StatsCard.vue';
3
+ import KpiCard from './KpiCard.vue';
4
+ import StatusBadge from './StatusBadge.vue';
5
+ import PriorityBadge from './PriorityBadge.vue';
6
+ import AgentLoadIndicator from './AgentLoadIndicator.vue';
7
+
8
+ export default {
9
+ title: 'Pages/AdminDashboard',
10
+ };
11
+
12
+ const sidebarLinks = [
13
+ {
14
+ label: 'Reports',
15
+ 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',
16
+ active: true,
17
+ },
18
+ {
19
+ label: 'Tickets',
20
+ 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',
21
+ badge: '24',
22
+ },
23
+ {
24
+ label: 'Departments',
25
+ 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',
26
+ },
27
+ { label: 'SLA Policies', icon: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z' },
28
+ {
29
+ label: 'Knowledge Base',
30
+ 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',
31
+ },
32
+ { label: 'Escalation Rules', icon: 'M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12' },
33
+ {
34
+ label: 'Tags',
35
+ 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',
36
+ },
37
+ {
38
+ label: 'Plugins',
39
+ 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',
40
+ },
41
+ {
42
+ label: 'Settings',
43
+ 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',
44
+ },
45
+ ];
46
+
47
+ const tickets = [
48
+ {
49
+ ref: 'ESC-1042',
50
+ subject: 'Unable to export CSV reports from dashboard',
51
+ requester: 'Sarah Chen',
52
+ email: 'sarah@acme.co',
53
+ status: 'open',
54
+ priority: 'high',
55
+ ago: '12m ago',
56
+ author: 'Agent Mike',
57
+ sla: 'green',
58
+ },
59
+ {
60
+ ref: 'ESC-1041',
61
+ subject: 'SSO login redirect loop on Firefox',
62
+ requester: 'James Wilson',
63
+ email: 'james@corp.io',
64
+ status: 'pending',
65
+ priority: 'urgent',
66
+ ago: '34m ago',
67
+ author: 'Sarah Chen',
68
+ sla: 'amber',
69
+ },
70
+ {
71
+ ref: 'ESC-1040',
72
+ subject: 'Custom field values not persisting after save',
73
+ requester: 'Maria Garcia',
74
+ email: 'maria@startup.dev',
75
+ status: 'open',
76
+ priority: 'medium',
77
+ ago: '1h ago',
78
+ author: 'Agent Lisa',
79
+ sla: 'green',
80
+ },
81
+ {
82
+ ref: 'ESC-1039',
83
+ subject: 'Webhook notifications delayed by 15+ minutes',
84
+ requester: 'Tom Baker',
85
+ email: 'tom@enterprise.com',
86
+ status: 'open',
87
+ priority: 'high',
88
+ ago: '2h ago',
89
+ author: 'Tom Baker',
90
+ sla: 'red',
91
+ },
92
+ {
93
+ ref: 'ESC-1038',
94
+ subject: 'Knowledge base search returning no results',
95
+ requester: 'Priya Patel',
96
+ email: 'priya@example.com',
97
+ status: 'solved',
98
+ priority: 'medium',
99
+ ago: '3h ago',
100
+ author: 'Agent Mike',
101
+ sla: '',
102
+ },
103
+ {
104
+ ref: 'ESC-1037',
105
+ subject: 'Feature request: Dark mode for customer portal',
106
+ requester: 'Alex Kim',
107
+ email: 'alex@design.co',
108
+ status: 'open',
109
+ priority: 'low',
110
+ ago: '5h ago',
111
+ author: 'Alex Kim',
112
+ sla: '',
113
+ },
114
+ ];
115
+
116
+ /**
117
+ * Full admin dashboard with sidebar, top bar, stats cards, and ticket list.
118
+ * Used for README hero screenshot.
119
+ */
120
+ export const FullDashboard = {
121
+ render: () => ({
122
+ components: { StatsCard, KpiCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
123
+ data: () => ({ sidebarLinks, tickets }),
124
+ template: `
125
+ <div style="display:flex; min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden;">
126
+ <!-- Sidebar -->
127
+ <aside style="width:240px; background:#0a0a0a; border-right:1px solid rgba(255,255,255,0.06); display:flex; flex-direction:column; flex-shrink:0;">
128
+ <!-- Logo -->
129
+ <div style="height:56px; display:flex; align-items:center; gap:10px; padding:0 16px;">
130
+ <div style="width:32px; height:32px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
131
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
132
+ <defs><linearGradient id="esc-rb" 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>
133
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb)"/></g>
134
+ </svg>
135
+ </div>
136
+ <div>
137
+ <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
138
+ <span style="margin-left:6px; background:rgba(255,255,255,0.08); padding:2px 6px; border-radius:4px; font-size:10px; font-weight:600; color:#737373;">ADMIN</span>
139
+ </div>
140
+ </div>
141
+
142
+ <!-- Nav links -->
143
+ <nav style="flex:1; padding:4px 10px; display:flex; flex-direction:column; gap:1px; overflow-y:auto;">
144
+ <a v-for="link in sidebarLinks" :key="link.label"
145
+ :style="{
146
+ display: 'flex', alignItems: 'center', gap: '10px',
147
+ padding: '7px 10px', borderRadius: '8px',
148
+ fontSize: '13px', fontWeight: '500', textDecoration: 'none',
149
+ color: link.active ? '#fff' : '#737373',
150
+ background: link.active ? 'rgba(255,255,255,0.08)' : 'transparent',
151
+ }">
152
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" :stroke="link.active ? '#fff' : '#737373'" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" :d="link.icon"/></svg>
153
+ {{ link.label }}
154
+ <span v-if="link.badge" style="margin-left:auto; background:rgba(6,182,212,0.2); color:#06b6d4; font-size:10px; font-weight:600; padding:1px 6px; border-radius:10px;">{{ link.badge }}</span>
155
+ </a>
156
+ </nav>
157
+
158
+ <!-- Bottom user -->
159
+ <div style="border-top:1px solid rgba(255,255,255,0.06); padding:10px;">
160
+ <div style="display:flex; align-items:center; gap:10px; background:rgba(255,255,255,0.03); border-radius:8px; padding:8px 10px;">
161
+ <div style="width:28px; height:28px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#a3a3a3;">J</div>
162
+ <span style="font-size:13px; color:#a3a3a3;">Jane Admin</span>
163
+ </div>
164
+ </div>
165
+ </aside>
166
+
167
+ <!-- Main -->
168
+ <div style="flex:1; display:flex; flex-direction:column; min-width:0;">
169
+ <!-- Top bar -->
170
+ <header style="height:48px; display:flex; align-items:center; border-bottom:1px solid rgba(255,255,255,0.06); background:rgba(0,0,0,0.8); padding:0 20px; backdrop-filter:blur(12px); flex-shrink:0;">
171
+ <span style="font-size:13px; font-weight:600;">Reports</span>
172
+ </header>
173
+
174
+ <!-- Content -->
175
+ <main style="flex:1; padding:20px; overflow-y:auto;">
176
+ <!-- KPI row -->
177
+ <div class="grid grid-cols-3 gap-4" style="margin-bottom:20px;">
178
+ <KpiCard label="Total Tickets" :value="1284" :trend="5" icon="M2.25 13.5h3.86a2.25 2.25 0 012.012 1.244l.256.512a2.25 2.25 0 002.013 1.244h3.218a2.25 2.25 0 002.013-1.244l.256-.512a2.25 2.25 0 012.013-1.244h3.859" />
179
+ <KpiCard label="Avg Response Time" value="1h 42m" :trend="-18" icon="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
180
+ <KpiCard label="CSAT Score" value="94%" :trend="3" icon="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
181
+ </div>
182
+
183
+ <!-- Stats row -->
184
+ <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
185
+ <StatsCard label="Open Tickets" :value="142" trend="+8% this week" color="blue" />
186
+ <StatsCard label="Resolved Today" :value="38" trend="+12% vs yesterday" color="green" />
187
+ <StatsCard label="Avg Response" value="2h 14m" trend="-18% improvement" color="cyan" />
188
+ <StatsCard label="SLA Breaches" :value="3" trend="-25% vs last week" color="red" />
189
+ </div>
190
+
191
+ <!-- Recent tickets table -->
192
+ <div style="border-radius:12px; border:1px solid rgba(255,255,255,0.06); background:rgba(23,23,23,0.6); overflow:hidden;">
193
+ <div style="padding:12px 16px; border-bottom:1px solid rgba(255,255,255,0.06); display:flex; align-items:center; justify-content:space-between;">
194
+ <span style="font-size:13px; font-weight:600;">Recent Tickets</span>
195
+ <span style="font-size:12px; color:#737373;">Last 24 hours</span>
196
+ </div>
197
+ <table style="width:100%; border-collapse:collapse;">
198
+ <thead>
199
+ <tr style="background:rgba(255,255,255,0.02);">
200
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Reference</th>
201
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Subject</th>
202
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Requester</th>
203
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Status</th>
204
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Priority</th>
205
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Last Reply</th>
206
+ </tr>
207
+ </thead>
208
+ <tbody>
209
+ <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid rgba(255,255,255,0.04);" class="hover:bg-white/[0.03]">
210
+ <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;">
211
+ <div style="display:flex; align-items:center; gap:6px;">
212
+ <span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span>
213
+ <span style="color:#22d3ee;">{{ t.ref }}</span>
214
+ </div>
215
+ </td>
216
+ <td style="padding:10px 14px; font-size:13px; color:#d4d4d4; max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
217
+ <td style="padding:10px 14px;">
218
+ <div style="font-size:13px; color:#a3a3a3;">{{ t.requester }}</div>
219
+ <div style="font-size:11px; color:#525252;">{{ t.email }}</div>
220
+ </td>
221
+ <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
222
+ <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
223
+ <td style="padding:10px 14px; white-space:nowrap;">
224
+ <div style="font-size:13px; color:#a3a3a3;">{{ t.ago }}</div>
225
+ <div style="font-size:11px; color:#525252;">{{ t.author }}</div>
226
+ </td>
227
+ </tr>
228
+ </tbody>
229
+ </table>
230
+ </div>
231
+ </main>
232
+ </div>
233
+ </div>
234
+ `,
235
+ }),
236
+ };
237
+
238
+ /**
239
+ * Light mode admin dashboard — same layout, light theme.
240
+ * Used for README hero screenshot.
241
+ */
242
+ export const FullDashboardLight = {
243
+ render: () => ({
244
+ components: { StatsCard, KpiCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
245
+ data: () => ({ sidebarLinks, tickets }),
246
+ provide: { 'esc-dark': computed(() => false) },
247
+ template: `
248
+ <div style="display:flex; min-height:720px; background:#f9fafb; color:#111827; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden; border: 1px solid #e5e7eb;">
249
+ <aside style="width:240px; background:#ffffff; border-right:1px solid #e5e7eb; display:flex; flex-direction:column; flex-shrink:0;">
250
+ <div style="height:56px; display:flex; align-items:center; gap:10px; padding:0 16px;">
251
+ <div style="width:32px; height:32px; border-radius:8px; background:#f3f4f6; display:flex; align-items:center; justify-content:center;">
252
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
253
+ <defs><linearGradient id="esc-rb-l" 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>
254
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb-l)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb-l)"/></g>
255
+ </svg>
256
+ </div>
257
+ <div>
258
+ <span style="font-size:13px; font-weight:700; letter-spacing:0.02em; color:#111827;">Escalated</span>
259
+ <span style="margin-left:6px; background:#eff6ff; padding:2px 6px; border-radius:4px; font-size:10px; font-weight:600; color:#3b82f6;">ADMIN</span>
260
+ </div>
261
+ </div>
262
+ <nav style="flex:1; padding:4px 10px; display:flex; flex-direction:column; gap:1px; overflow-y:auto;">
263
+ <a v-for="link in sidebarLinks" :key="link.label" :style="{ display:'flex', alignItems:'center', gap:'10px', padding:'7px 10px', borderRadius:'8px', fontSize:'13px', fontWeight:'500', textDecoration:'none', color: link.active ? '#111827' : '#6b7280', background: link.active ? '#eff6ff' : 'transparent' }">
264
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" :stroke="link.active ? '#3b82f6' : '#9ca3af'" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" :d="link.icon"/></svg>
265
+ {{ link.label }}
266
+ <span v-if="link.badge" style="margin-left:auto; background:#dbeafe; color:#2563eb; font-size:10px; font-weight:600; padding:1px 6px; border-radius:10px;">{{ link.badge }}</span>
267
+ </a>
268
+ </nav>
269
+ <div style="border-top:1px solid #e5e7eb; padding:10px;">
270
+ <div style="display:flex; align-items:center; gap:10px; background:#f9fafb; border-radius:8px; padding:8px 10px;">
271
+ <div style="width:28px; height:28px; border-radius:6px; background:#e5e7eb; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#374151;">J</div>
272
+ <span style="font-size:13px; color:#374151;">Jane Admin</span>
273
+ </div>
274
+ </div>
275
+ </aside>
276
+ <div style="flex:1; display:flex; flex-direction:column; min-width:0;">
277
+ <header style="height:48px; display:flex; align-items:center; border-bottom:1px solid #e5e7eb; background:rgba(255,255,255,0.95); padding:0 20px; backdrop-filter:blur(12px); flex-shrink:0;">
278
+ <span style="font-size:13px; font-weight:600; color:#111827;">Reports</span>
279
+ </header>
280
+ <main style="flex:1; padding:20px; overflow-y:auto;">
281
+ <div class="grid grid-cols-3 gap-4" style="margin-bottom:20px;">
282
+ <KpiCard label="Total Tickets" :value="1284" :trend="5" icon="M2.25 13.5h3.86a2.25 2.25 0 012.012 1.244l.256.512a2.25 2.25 0 002.013 1.244h3.218a2.25 2.25 0 002.013-1.244l.256-.512a2.25 2.25 0 012.013-1.244h3.859" />
283
+ <KpiCard label="Avg Response Time" value="1h 42m" :trend="-18" icon="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
284
+ <KpiCard label="CSAT Score" value="94%" :trend="3" icon="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
285
+ </div>
286
+ <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
287
+ <StatsCard label="Open Tickets" :value="142" trend="+8% this week" color="blue" />
288
+ <StatsCard label="Resolved Today" :value="38" trend="+12% vs yesterday" color="green" />
289
+ <StatsCard label="Avg Response" value="2h 14m" trend="-18% improvement" color="cyan" />
290
+ <StatsCard label="SLA Breaches" :value="3" trend="-25% vs last week" color="red" />
291
+ </div>
292
+ <div style="border-radius:12px; border:1px solid #e5e7eb; background:#ffffff; overflow:hidden;">
293
+ <div style="padding:12px 16px; border-bottom:1px solid #e5e7eb; display:flex; align-items:center; justify-content:space-between;">
294
+ <span style="font-size:13px; font-weight:600; color:#111827;">Recent Tickets</span>
295
+ <span style="font-size:12px; color:#9ca3af;">Last 24 hours</span>
296
+ </div>
297
+ <table style="width:100%; border-collapse:collapse;">
298
+ <thead><tr style="background:#f9fafb;">
299
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Reference</th>
300
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Subject</th>
301
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Requester</th>
302
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Status</th>
303
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Priority</th>
304
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Last Reply</th>
305
+ </tr></thead>
306
+ <tbody>
307
+ <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid #f3f4f6;">
308
+ <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;"><div style="display:flex; align-items:center; gap:6px;"><span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span><span style="color:#2563eb;">{{ t.ref }}</span></div></td>
309
+ <td style="padding:10px 14px; font-size:13px; color:#111827; max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
310
+ <td style="padding:10px 14px;"><div style="font-size:13px; color:#374151;">{{ t.requester }}</div><div style="font-size:11px; color:#9ca3af;">{{ t.email }}</div></td>
311
+ <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
312
+ <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
313
+ <td style="padding:10px 14px; white-space:nowrap;"><div style="font-size:13px; color:#374151;">{{ t.ago }}</div><div style="font-size:11px; color:#9ca3af;">{{ t.author }}</div></td>
314
+ </tr>
315
+ </tbody>
316
+ </table>
317
+ </div>
318
+ </main>
319
+ </div>
320
+ </div>
321
+ `,
322
+ }),
323
+ };
324
+
325
+ /**
326
+ * Agent panel view with top navigation bar, dashboard stats, and ticket list.
327
+ * Used for README hero screenshot.
328
+ */
329
+ export const AgentPanel = {
330
+ render: () => ({
331
+ components: { StatsCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
332
+ data: () => ({
333
+ tickets: [
334
+ {
335
+ ref: 'ESC-1042',
336
+ subject: 'Unable to export CSV reports from dashboard',
337
+ requester: 'Sarah Chen',
338
+ status: 'open',
339
+ priority: 'high',
340
+ ago: '12m ago',
341
+ sla: 'green',
342
+ },
343
+ {
344
+ ref: 'ESC-1041',
345
+ subject: 'SSO login redirect loop on Firefox',
346
+ requester: 'James Wilson',
347
+ status: 'pending',
348
+ priority: 'urgent',
349
+ ago: '34m ago',
350
+ sla: 'amber',
351
+ },
352
+ {
353
+ ref: 'ESC-1040',
354
+ subject: 'Custom field values not persisting after save',
355
+ requester: 'Maria Garcia',
356
+ status: 'open',
357
+ priority: 'medium',
358
+ ago: '1h ago',
359
+ sla: 'green',
360
+ },
361
+ {
362
+ ref: 'ESC-1039',
363
+ subject: 'Webhook notifications delayed by 15+ minutes',
364
+ requester: 'Tom Baker',
365
+ status: 'open',
366
+ priority: 'high',
367
+ ago: '2h ago',
368
+ sla: 'red',
369
+ },
370
+ {
371
+ ref: 'ESC-1038',
372
+ subject: 'Knowledge base search returning no results',
373
+ requester: 'Priya Patel',
374
+ status: 'solved',
375
+ priority: 'medium',
376
+ ago: '3h ago',
377
+ sla: '',
378
+ },
379
+ ],
380
+ }),
381
+ template: `
382
+ <div style="min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden;">
383
+ <!-- Top nav -->
384
+ <nav style="height:48px; border-bottom:1px solid rgba(255,255,255,0.06); background:#0a0a0a; display:flex; align-items:center; justify-content:space-between; padding:0 24px;">
385
+ <div style="display:flex; align-items:center; gap:10px;">
386
+ <div style="width:30px; height:30px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
387
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
388
+ <defs><linearGradient id="esc-rb2" 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>
389
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb2)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb2)"/></g>
390
+ </svg>
391
+ </div>
392
+ <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
393
+ </div>
394
+ <div style="display:flex; align-items:center; gap:4px;">
395
+ <span style="background:rgba(255,255,255,0.08); padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Dashboard</span>
396
+ <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#737373;">Tickets</span>
397
+ </div>
398
+ <div style="display:flex; align-items:center; gap:12px;">
399
+ <span style="font-size:13px; color:#737373;">Admin</span>
400
+ <div style="width:1px; height:16px; background:rgba(255,255,255,0.08);"></div>
401
+ <div style="display:flex; align-items:center; gap:8px;">
402
+ <div style="width:26px; height:26px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#a3a3a3;">M</div>
403
+ <span style="font-size:13px; color:#a3a3a3;">Mike Agent</span>
404
+ </div>
405
+ </div>
406
+ </nav>
407
+
408
+ <!-- Content -->
409
+ <main style="max-width:1100px; margin:0 auto; padding:20px 24px;">
410
+ <!-- Stats -->
411
+ <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
412
+ <StatsCard label="My Open" :value="18" trend="+2 today" color="blue" />
413
+ <StatsCard label="Unassigned" :value="7" trend="3 urgent" color="red" />
414
+ <StatsCard label="Avg Response" value="1h 08m" trend="-22% this week" color="cyan" />
415
+ <StatsCard label="Resolved Today" :value="12" trend="+4 vs yesterday" color="green" />
416
+ </div>
417
+
418
+ <!-- My tickets table -->
419
+ <div style="border-radius:12px; border:1px solid rgba(255,255,255,0.06); background:rgba(23,23,23,0.6); overflow:hidden;">
420
+ <div style="padding:12px 16px; border-bottom:1px solid rgba(255,255,255,0.06); display:flex; align-items:center; justify-content:space-between;">
421
+ <span style="font-size:13px; font-weight:600;">My Tickets</span>
422
+ <span style="font-size:12px; color:#737373;">Assigned to me</span>
423
+ </div>
424
+ <table style="width:100%; border-collapse:collapse;">
425
+ <thead>
426
+ <tr style="background:rgba(255,255,255,0.02);">
427
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Reference</th>
428
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Subject</th>
429
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Requester</th>
430
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Status</th>
431
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Priority</th>
432
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252;">Updated</th>
433
+ </tr>
434
+ </thead>
435
+ <tbody>
436
+ <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid rgba(255,255,255,0.04);">
437
+ <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;">
438
+ <div style="display:flex; align-items:center; gap:6px;">
439
+ <span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span>
440
+ <span style="color:#22d3ee;">{{ t.ref }}</span>
441
+ </div>
442
+ </td>
443
+ <td style="padding:10px 14px; font-size:13px; color:#d4d4d4; max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
444
+ <td style="padding:10px 14px; font-size:13px; color:#a3a3a3;">{{ t.requester }}</td>
445
+ <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
446
+ <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
447
+ <td style="padding:10px 14px; font-size:13px; color:#a3a3a3; white-space:nowrap;">{{ t.ago }}</td>
448
+ </tr>
449
+ </tbody>
450
+ </table>
451
+ </div>
452
+ </main>
453
+ </div>
454
+ `,
455
+ }),
456
+ };
457
+
458
+ /**
459
+ * Ticket detail view — agent viewing a single ticket with conversation thread and sidebar.
460
+ * Dark mode. Used for README hero screenshot.
461
+ */
462
+ export const TicketDetailView = {
463
+ render: () => ({
464
+ components: { StatusBadge, PriorityBadge },
465
+ data: () => ({
466
+ messages: [
467
+ {
468
+ id: 1,
469
+ author: 'Sarah Chen',
470
+ initial: 'S',
471
+ role: 'customer',
472
+ time: 'Mar 20, 2026 at 2:14 PM',
473
+ text: "Hi, I'm trying to export our monthly CSV report from the dashboard but the download never starts. I've tried Chrome and Firefox, both on macOS. The button spinner appears but then nothing happens after about 30 seconds. This is blocking our end-of-quarter review.",
474
+ },
475
+ {
476
+ id: 2,
477
+ author: 'Mike Agent',
478
+ initial: 'M',
479
+ role: 'agent',
480
+ time: 'Mar 20, 2026 at 2:32 PM',
481
+ text: "Hi Sarah, thanks for reporting this. I can reproduce the issue on our end. It looks like exports with more than 10,000 rows are timing out. I've escalated this to our engineering team and we're working on a fix. In the meantime, could you try filtering to a smaller date range as a workaround?",
482
+ },
483
+ {
484
+ id: 3,
485
+ author: 'Sarah Chen',
486
+ initial: 'S',
487
+ role: 'customer',
488
+ time: 'Mar 20, 2026 at 3:05 PM',
489
+ text: 'Thanks Mike! Filtering to a single month did work as a workaround. That said, we really need the full quarter export for our finance team. Is there an ETA on the fix?',
490
+ },
491
+ {
492
+ id: 4,
493
+ author: 'Mike Agent',
494
+ initial: 'M',
495
+ role: 'agent',
496
+ time: 'Mar 20, 2026 at 3:18 PM',
497
+ text: "Great to hear the workaround helped! Engineering has identified the root cause — it's a memory limit on the export worker. The fix is in review and should ship by tomorrow morning. I'll update this ticket as soon as it's deployed.",
498
+ },
499
+ ],
500
+ activeTab: 'reply',
501
+ }),
502
+ template: `
503
+ <div style="min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden;">
504
+ <!-- Top nav (same as AgentPanel) -->
505
+ <nav style="height:48px; border-bottom:1px solid rgba(255,255,255,0.06); background:#0a0a0a; display:flex; align-items:center; justify-content:space-between; padding:0 24px; flex-shrink:0;">
506
+ <div style="display:flex; align-items:center; gap:10px;">
507
+ <div style="width:30px; height:30px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
508
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
509
+ <defs><linearGradient id="esc-rb3" 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>
510
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb3)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb3)"/></g>
511
+ </svg>
512
+ </div>
513
+ <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
514
+ </div>
515
+ <div style="display:flex; align-items:center; gap:4px;">
516
+ <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#737373;">Dashboard</span>
517
+ <span style="background:rgba(255,255,255,0.08); padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Tickets</span>
518
+ </div>
519
+ <div style="display:flex; align-items:center; gap:12px;">
520
+ <span style="font-size:13px; color:#737373;">Admin</span>
521
+ <div style="width:1px; height:16px; background:rgba(255,255,255,0.08);"></div>
522
+ <div style="display:flex; align-items:center; gap:8px;">
523
+ <div style="width:26px; height:26px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#a3a3a3;">M</div>
524
+ <span style="font-size:13px; color:#a3a3a3;">Mike Agent</span>
525
+ </div>
526
+ </div>
527
+ </nav>
528
+
529
+ <!-- Main content area: left (thread) + right (sidebar) -->
530
+ <div style="display:flex; flex:1; min-height:672px;">
531
+ <!-- Left: ticket thread -->
532
+ <div style="flex:7; display:flex; flex-direction:column; border-right:1px solid rgba(255,255,255,0.06);">
533
+ <!-- Ticket header -->
534
+ <div style="padding:16px 24px; border-bottom:1px solid rgba(255,255,255,0.06);">
535
+ <div style="display:flex; align-items:center; gap:8px; margin-bottom:6px;">
536
+ <span style="font-size:13px; color:#22d3ee; font-weight:500;">ESC-1042</span>
537
+ <StatusBadge status="in_progress" />
538
+ <PriorityBadge priority="high" />
539
+ </div>
540
+ <h1 style="font-size:16px; font-weight:600; margin:0; color:#f5f5f5;">Unable to export CSV reports from dashboard</h1>
541
+ </div>
542
+
543
+ <!-- Conversation thread -->
544
+ <div style="flex:1; overflow-y:auto; padding:16px 24px; display:flex; flex-direction:column; gap:16px;">
545
+ <div v-for="msg in messages" :key="msg.id" style="display:flex; gap:12px;">
546
+ <!-- Avatar -->
547
+ <div :style="{
548
+ width: '32px', height: '32px', borderRadius: '8px', flexShrink: 0,
549
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
550
+ fontSize: '12px', fontWeight: '600',
551
+ background: msg.role === 'agent' ? 'rgba(59,130,246,0.15)' : 'rgba(255,255,255,0.08)',
552
+ color: msg.role === 'agent' ? '#60a5fa' : '#a3a3a3',
553
+ }">{{ msg.initial }}</div>
554
+ <!-- Message body -->
555
+ <div style="flex:1; min-width:0;">
556
+ <div style="display:flex; align-items:center; gap:8px; margin-bottom:4px;">
557
+ <span style="font-size:13px; font-weight:600;" :style="{ color: msg.role === 'agent' ? '#60a5fa' : '#e5e5e5' }">{{ msg.author }}</span>
558
+ <span v-if="msg.role === 'agent'" style="font-size:10px; background:rgba(59,130,246,0.12); color:#60a5fa; padding:1px 6px; border-radius:4px; font-weight:500;">Agent</span>
559
+ <span style="font-size:11px; color:#525252;">{{ msg.time }}</span>
560
+ </div>
561
+ <div style="font-size:13px; line-height:1.6; color:#d4d4d4; background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.04); border-radius:10px; padding:10px 14px;">
562
+ {{ msg.text }}
563
+ </div>
564
+ </div>
565
+ </div>
566
+ </div>
567
+
568
+ <!-- Reply composer -->
569
+ <div style="border-top:1px solid rgba(255,255,255,0.06); padding:12px 24px;">
570
+ <div style="display:flex; gap:0; margin-bottom:8px;">
571
+ <button :style="{
572
+ padding: '5px 14px', fontSize: '12px', fontWeight: '600', border: 'none', cursor: 'pointer', borderRadius: '6px 0 0 6px',
573
+ background: activeTab === 'reply' ? 'rgba(59,130,246,0.15)' : 'rgba(255,255,255,0.05)',
574
+ color: activeTab === 'reply' ? '#60a5fa' : '#737373',
575
+ }" @click="activeTab = 'reply'">Reply</button>
576
+ <button :style="{
577
+ padding: '5px 14px', fontSize: '12px', fontWeight: '600', border: 'none', cursor: 'pointer', borderRadius: '0 6px 6px 0',
578
+ background: activeTab === 'note' ? 'rgba(234,179,8,0.12)' : 'rgba(255,255,255,0.05)',
579
+ color: activeTab === 'note' ? '#facc15' : '#737373',
580
+ }" @click="activeTab = 'note'">Internal Note</button>
581
+ </div>
582
+ <div style="position:relative;">
583
+ <textarea :placeholder="activeTab === 'reply' ? 'Type your reply...' : 'Add an internal note...'" style="width:100%; min-height:68px; background:rgba(255,255,255,0.04); border:1px solid rgba(255,255,255,0.08); border-radius:8px; padding:10px 14px; font-size:13px; color:#e5e5e5; resize:none; font-family:inherit; box-sizing:border-box;" />
584
+ <div style="display:flex; justify-content:flex-end; margin-top:8px;">
585
+ <span :style="{
586
+ padding: '6px 16px', borderRadius: '8px', fontSize: '12px', fontWeight: '600', cursor: 'pointer',
587
+ background: activeTab === 'reply' ? '#3b82f6' : '#ca8a04',
588
+ color: '#fff',
589
+ }">{{ activeTab === 'reply' ? 'Send Reply' : 'Add Note' }}</span>
590
+ </div>
591
+ </div>
592
+ </div>
593
+ </div>
594
+
595
+ <!-- Right: ticket sidebar -->
596
+ <div style="flex:3; padding:16px; overflow-y:auto; background:rgba(10,10,10,0.5);">
597
+ <!-- Assignee -->
598
+ <div style="margin-bottom:20px;">
599
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Assignee</div>
600
+ <div style="display:flex; align-items:center; gap:8px;">
601
+ <div style="width:28px; height:28px; border-radius:6px; background:rgba(59,130,246,0.15); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#60a5fa;">M</div>
602
+ <div>
603
+ <div style="font-size:13px; font-weight:500; color:#e5e5e5;">Mike Agent</div>
604
+ <div style="font-size:11px; color:#525252;">Support Team</div>
605
+ </div>
606
+ </div>
607
+ </div>
608
+
609
+ <!-- Department -->
610
+ <div style="margin-bottom:20px;">
611
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Department</div>
612
+ <div style="font-size:13px; color:#a3a3a3;">Technical Support</div>
613
+ </div>
614
+
615
+ <!-- Tags -->
616
+ <div style="margin-bottom:20px;">
617
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Tags</div>
618
+ <div style="display:flex; flex-wrap:wrap; gap:4px;">
619
+ <span style="background:rgba(255,255,255,0.06); color:#a3a3a3; font-size:11px; padding:2px 8px; border-radius:6px;">csv-export</span>
620
+ <span style="background:rgba(255,255,255,0.06); color:#a3a3a3; font-size:11px; padding:2px 8px; border-radius:6px;">dashboard</span>
621
+ <span style="background:rgba(255,255,255,0.06); color:#a3a3a3; font-size:11px; padding:2px 8px; border-radius:6px;">bug</span>
622
+ </div>
623
+ </div>
624
+
625
+ <!-- SLA Timer -->
626
+ <div style="margin-bottom:20px;">
627
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">SLA</div>
628
+ <div style="border:1px solid rgba(16,185,129,0.2); background:rgba(16,185,129,0.08); border-radius:8px; padding:8px 12px;">
629
+ <div style="font-size:12px; font-weight:500; color:#34d399;">Next Response Due</div>
630
+ <div style="font-size:11px; color:#34d399;">3h 42m remaining</div>
631
+ </div>
632
+ </div>
633
+
634
+ <!-- Requester Info -->
635
+ <div style="margin-bottom:20px;">
636
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Requester</div>
637
+ <div style="background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.06); border-radius:8px; padding:10px 12px;">
638
+ <div style="display:flex; align-items:center; gap:8px; margin-bottom:8px;">
639
+ <div style="width:28px; height:28px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#a3a3a3;">S</div>
640
+ <div>
641
+ <div style="font-size:13px; font-weight:500; color:#e5e5e5;">Sarah Chen</div>
642
+ <div style="font-size:11px; color:#525252;">sarah@acme.co</div>
643
+ </div>
644
+ </div>
645
+ <div style="display:flex; justify-content:space-between; font-size:11px; color:#525252; border-top:1px solid rgba(255,255,255,0.04); padding-top:8px;">
646
+ <span>Org: Acme Corp</span>
647
+ <span>12 tickets</span>
648
+ </div>
649
+ </div>
650
+ </div>
651
+
652
+ <!-- Created / Updated -->
653
+ <div>
654
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Details</div>
655
+ <div style="font-size:12px; color:#525252; display:flex; flex-direction:column; gap:4px;">
656
+ <div style="display:flex; justify-content:space-between;"><span>Created</span><span style="color:#737373;">Mar 20, 2026</span></div>
657
+ <div style="display:flex; justify-content:space-between;"><span>Updated</span><span style="color:#737373;">2 min ago</span></div>
658
+ <div style="display:flex; justify-content:space-between;"><span>Channel</span><span style="color:#737373;">Email</span></div>
659
+ </div>
660
+ </div>
661
+ </div>
662
+ </div>
663
+ </div>
664
+ `,
665
+ }),
666
+ };
667
+
668
+ export const AgentPanelLight = {
669
+ render: () => ({
670
+ components: { StatsCard, StatusBadge, PriorityBadge, AgentLoadIndicator },
671
+ provide: { 'esc-dark': computed(() => false) },
672
+ data: () => ({
673
+ tickets: [
674
+ {
675
+ ref: 'ESC-1042',
676
+ subject: 'Unable to export CSV reports from dashboard',
677
+ requester: 'Sarah Chen',
678
+ status: 'open',
679
+ priority: 'high',
680
+ ago: '12m ago',
681
+ sla: 'green',
682
+ },
683
+ {
684
+ ref: 'ESC-1041',
685
+ subject: 'SSO login redirect loop on Firefox',
686
+ requester: 'James Wilson',
687
+ status: 'pending',
688
+ priority: 'urgent',
689
+ ago: '34m ago',
690
+ sla: 'amber',
691
+ },
692
+ {
693
+ ref: 'ESC-1040',
694
+ subject: 'Custom field values not persisting after save',
695
+ requester: 'Maria Garcia',
696
+ status: 'open',
697
+ priority: 'medium',
698
+ ago: '1h ago',
699
+ sla: 'green',
700
+ },
701
+ {
702
+ ref: 'ESC-1039',
703
+ subject: 'Webhook notifications delayed by 15+ minutes',
704
+ requester: 'Tom Baker',
705
+ status: 'open',
706
+ priority: 'high',
707
+ ago: '2h ago',
708
+ sla: 'red',
709
+ },
710
+ {
711
+ ref: 'ESC-1038',
712
+ subject: 'Knowledge base search returning no results',
713
+ requester: 'Priya Patel',
714
+ status: 'solved',
715
+ priority: 'medium',
716
+ ago: '3h ago',
717
+ sla: '',
718
+ },
719
+ ],
720
+ }),
721
+ template: `
722
+ <div style="min-height:720px; background:#f9fafb; color:#111827; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden; border: 1px solid #e5e7eb;">
723
+ <!-- Top nav -->
724
+ <nav style="height:48px; border-bottom:1px solid #e5e7eb; background:#ffffff; display:flex; align-items:center; justify-content:space-between; padding:0 24px;">
725
+ <div style="display:flex; align-items:center; gap:10px;">
726
+ <div style="width:30px; height:30px; border-radius:8px; background:#f3f4f6; display:flex; align-items:center; justify-content:center;">
727
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
728
+ <defs><linearGradient id="esc-rb2l" 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>
729
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb2l)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb2l)"/></g>
730
+ </svg>
731
+ </div>
732
+ <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
733
+ </div>
734
+ <div style="display:flex; align-items:center; gap:4px;">
735
+ <span style="background:#eff6ff; padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Dashboard</span>
736
+ <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#6b7280;">Tickets</span>
737
+ </div>
738
+ <div style="display:flex; align-items:center; gap:12px;">
739
+ <span style="font-size:13px; color:#6b7280;">Admin</span>
740
+ <div style="width:1px; height:16px; background:#eff6ff;"></div>
741
+ <div style="display:flex; align-items:center; gap:8px;">
742
+ <div style="width:26px; height:26px; border-radius:6px; background:#eff6ff; display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#374151;">M</div>
743
+ <span style="font-size:13px; color:#374151;">Mike Agent</span>
744
+ </div>
745
+ </div>
746
+ </nav>
747
+
748
+ <!-- Content -->
749
+ <main style="max-width:1100px; margin:0 auto; padding:20px 24px;">
750
+ <!-- Stats -->
751
+ <div class="grid grid-cols-4 gap-4" style="margin-bottom:20px;">
752
+ <StatsCard label="My Open" :value="18" trend="+2 today" color="blue" />
753
+ <StatsCard label="Unassigned" :value="7" trend="3 urgent" color="red" />
754
+ <StatsCard label="Avg Response" value="1h 08m" trend="-22% this week" color="cyan" />
755
+ <StatsCard label="Resolved Today" :value="12" trend="+4 vs yesterday" color="green" />
756
+ </div>
757
+
758
+ <!-- My tickets table -->
759
+ <div style="border-radius:12px; border:1px solid #e5e7eb; background:#ffffff; overflow:hidden;">
760
+ <div style="padding:12px 16px; border-bottom:1px solid #e5e7eb; display:flex; align-items:center; justify-content:space-between;">
761
+ <span style="font-size:13px; font-weight:600;">My Tickets</span>
762
+ <span style="font-size:12px; color:#6b7280;">Assigned to me</span>
763
+ </div>
764
+ <table style="width:100%; border-collapse:collapse;">
765
+ <thead>
766
+ <tr style="background:#f3f4f6;">
767
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Reference</th>
768
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Subject</th>
769
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Requester</th>
770
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Status</th>
771
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Priority</th>
772
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#6b7280;">Updated</th>
773
+ </tr>
774
+ </thead>
775
+ <tbody>
776
+ <tr v-for="t in tickets" :key="t.ref" style="border-top:1px solid #f3f4f6;">
777
+ <td style="padding:10px 14px; font-size:13px; font-weight:500; white-space:nowrap;">
778
+ <div style="display:flex; align-items:center; gap:6px;">
779
+ <span v-if="t.sla" :style="{ width:'7px', height:'7px', borderRadius:'50%', background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : '#22c55e' }"></span>
780
+ <span style="color:#2563eb;">{{ t.ref }}</span>
781
+ </div>
782
+ </td>
783
+ <td style="padding:10px 14px; font-size:13px; color:#111827; max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ t.subject }}</td>
784
+ <td style="padding:10px 14px; font-size:13px; color:#374151;">{{ t.requester }}</td>
785
+ <td style="padding:10px 14px;"><StatusBadge :status="t.status" /></td>
786
+ <td style="padding:10px 14px;"><PriorityBadge :priority="t.priority" /></td>
787
+ <td style="padding:10px 14px; font-size:13px; color:#374151; white-space:nowrap;">{{ t.ago }}</td>
788
+ </tr>
789
+ </tbody>
790
+ </table>
791
+ </div>
792
+ </main>
793
+ </div>
794
+ `,
795
+ }),
796
+ };
797
+
798
+ /**
799
+ * Ticket list view — agent ticket queue with filters, search, table, and pagination.
800
+ * Used for README hero screenshot.
801
+ */
802
+ export const TicketListView = {
803
+ render: () => ({
804
+ components: { StatusBadge, PriorityBadge },
805
+ data: () => ({
806
+ chips: [
807
+ { label: 'My Tickets', active: true },
808
+ { label: 'Unassigned', active: false },
809
+ { label: 'Urgent+', active: false },
810
+ { label: 'SLA Breaching', active: false },
811
+ { label: 'Following', active: false },
812
+ ],
813
+ tickets: [
814
+ {
815
+ ref: 'ESC-1042',
816
+ subject: 'Unable to export CSV reports from dashboard',
817
+ requester: 'Sarah Chen',
818
+ email: 'sarah@acme.co',
819
+ status: 'open',
820
+ priority: 'high',
821
+ assignee: 'Mike Agent',
822
+ ago: '12m ago',
823
+ author: 'Agent Mike',
824
+ sla: 'green',
825
+ checked: true,
826
+ },
827
+ {
828
+ ref: 'ESC-1041',
829
+ subject: 'SSO login redirect loop on Firefox',
830
+ requester: 'James Wilson',
831
+ email: 'james@corp.io',
832
+ status: 'pending',
833
+ priority: 'urgent',
834
+ assignee: 'Lisa Torres',
835
+ ago: '34m ago',
836
+ author: 'James Wilson',
837
+ sla: 'amber',
838
+ checked: true,
839
+ },
840
+ {
841
+ ref: 'ESC-1040',
842
+ subject: 'Custom field values not persisting after save',
843
+ requester: 'Maria Garcia',
844
+ email: 'maria@startup.dev',
845
+ status: 'in_progress',
846
+ priority: 'medium',
847
+ assignee: 'Mike Agent',
848
+ ago: '1h ago',
849
+ author: 'Agent Lisa',
850
+ sla: 'green',
851
+ checked: false,
852
+ },
853
+ {
854
+ ref: 'ESC-1039',
855
+ subject: 'Webhook notifications delayed by 15+ minutes',
856
+ requester: 'Tom Baker',
857
+ email: 'tom@enterprise.com',
858
+ status: 'escalated',
859
+ priority: 'high',
860
+ assignee: 'Mike Agent',
861
+ ago: '2h ago',
862
+ author: 'Tom Baker',
863
+ sla: 'red',
864
+ checked: false,
865
+ },
866
+ {
867
+ ref: 'ESC-1038',
868
+ subject: 'Knowledge base search returning no results',
869
+ requester: 'Priya Patel',
870
+ email: 'priya@example.com',
871
+ status: 'solved',
872
+ priority: 'medium',
873
+ assignee: 'Lisa Torres',
874
+ ago: '3h ago',
875
+ author: 'Agent Mike',
876
+ sla: '',
877
+ checked: false,
878
+ },
879
+ {
880
+ ref: 'ESC-1037',
881
+ subject: 'Feature request: Dark mode for customer portal',
882
+ requester: 'Alex Kim',
883
+ email: 'alex@design.co',
884
+ status: 'open',
885
+ priority: 'low',
886
+ assignee: 'Unassigned',
887
+ ago: '5h ago',
888
+ author: 'Alex Kim',
889
+ sla: '',
890
+ checked: false,
891
+ },
892
+ {
893
+ ref: 'ESC-1036',
894
+ subject: 'Mobile app crashes when uploading attachments',
895
+ requester: 'David Liu',
896
+ email: 'david@bigcorp.com',
897
+ status: 'in_progress',
898
+ priority: 'urgent',
899
+ assignee: 'Mike Agent',
900
+ ago: '6h ago',
901
+ author: 'Agent Mike',
902
+ sla: 'amber',
903
+ checked: false,
904
+ },
905
+ {
906
+ ref: 'ESC-1035',
907
+ subject: 'Automation rule not triggering on status change',
908
+ requester: 'Nina Petrova',
909
+ email: 'nina@saas.io',
910
+ status: 'pending',
911
+ priority: 'medium',
912
+ assignee: 'Lisa Torres',
913
+ ago: '8h ago',
914
+ author: 'Nina Petrova',
915
+ sla: 'green',
916
+ checked: false,
917
+ },
918
+ ],
919
+ }),
920
+ template: `
921
+ <div style="min-height:720px; background:#000; color:#fff; font-family: ui-sans-serif, system-ui, sans-serif; border-radius: 12px; overflow: hidden; display:flex; flex-direction:column;">
922
+ <!-- Top nav (same as AgentPanel with Tickets active) -->
923
+ <nav style="height:48px; border-bottom:1px solid rgba(255,255,255,0.06); background:#0a0a0a; display:flex; align-items:center; justify-content:space-between; padding:0 24px; flex-shrink:0;">
924
+ <div style="display:flex; align-items:center; gap:10px;">
925
+ <div style="width:30px; height:30px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
926
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
927
+ <defs><linearGradient id="esc-rb4" 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>
928
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb4)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb4)"/></g>
929
+ </svg>
930
+ </div>
931
+ <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
932
+ </div>
933
+ <div style="display:flex; align-items:center; gap:4px;">
934
+ <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#737373;">Dashboard</span>
935
+ <span style="background:rgba(255,255,255,0.08); padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500;">Tickets</span>
936
+ </div>
937
+ <div style="display:flex; align-items:center; gap:12px;">
938
+ <span style="font-size:13px; color:#737373;">Admin</span>
939
+ <div style="width:1px; height:16px; background:rgba(255,255,255,0.08);"></div>
940
+ <div style="display:flex; align-items:center; gap:8px;">
941
+ <div style="width:26px; height:26px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#a3a3a3;">M</div>
942
+ <span style="font-size:13px; color:#a3a3a3;">Mike Agent</span>
943
+ </div>
944
+ </div>
945
+ </nav>
946
+
947
+ <!-- Content area -->
948
+ <div style="flex:1; display:flex; justify-content:center; padding:20px 24px; overflow-y:auto;">
949
+ <div style="max-width:1100px; width:100%; display:flex; flex-direction:column; gap:16px;">
950
+
951
+ <!-- Quick filter chips -->
952
+ <div style="display:flex; gap:8px; flex-wrap:wrap;">
953
+ <span v-for="chip in chips" :key="chip.label"
954
+ :style="{
955
+ padding: '4px 14px',
956
+ borderRadius: '20px',
957
+ fontSize: '12px',
958
+ fontWeight: '500',
959
+ cursor: 'pointer',
960
+ border: chip.active ? '1px solid rgba(6,182,212,0.4)' : '1px solid rgba(255,255,255,0.08)',
961
+ background: chip.active ? 'rgba(6,182,212,0.15)' : 'rgba(255,255,255,0.04)',
962
+ color: chip.active ? '#22d3ee' : '#a3a3a3',
963
+ }">
964
+ {{ chip.label }}
965
+ </span>
966
+ </div>
967
+
968
+ <!-- Search / filter bar -->
969
+ <div style="display:flex; align-items:center; gap:10px; flex-wrap:wrap;">
970
+ <div style="flex:1; min-width:200px; position:relative;">
971
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#737373" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="position:absolute; left:10px; top:50%; transform:translateY(-50%);"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
972
+ <input type="text" placeholder="Search tickets..." style="width:100%; padding:7px 12px 7px 32px; border-radius:8px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#fff; font-size:13px; outline:none;" />
973
+ </div>
974
+ <select style="padding:7px 10px; border-radius:8px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:12px; outline:none; appearance:auto;">
975
+ <option>Status</option>
976
+ <option>Open</option>
977
+ <option>Pending</option>
978
+ <option>In Progress</option>
979
+ <option>Solved</option>
980
+ <option>Escalated</option>
981
+ </select>
982
+ <select style="padding:7px 10px; border-radius:8px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:12px; outline:none; appearance:auto;">
983
+ <option>Priority</option>
984
+ <option>Low</option>
985
+ <option>Medium</option>
986
+ <option>High</option>
987
+ <option>Urgent</option>
988
+ </select>
989
+ <span style="font-size:12px; color:#06b6d4; cursor:pointer; font-weight:500; padding:4px 8px;">Advanced</span>
990
+ </div>
991
+
992
+ <!-- Ticket table -->
993
+ <div style="border-radius:12px; border:1px solid rgba(255,255,255,0.06); background:rgba(23,23,23,0.6); overflow:hidden; flex:1;">
994
+ <table style="width:100%; border-collapse:collapse; font-size:13px;">
995
+ <thead>
996
+ <tr style="border-bottom:1px solid rgba(255,255,255,0.06);">
997
+ <th style="padding:10px 14px; text-align:left; width:28px;">
998
+ <div style="width:14px; height:14px; border:1px solid rgba(255,255,255,0.2); border-radius:3px;"></div>
999
+ </th>
1000
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Reference</th>
1001
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Subject</th>
1002
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Requester</th>
1003
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Status</th>
1004
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Priority</th>
1005
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Assignee</th>
1006
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Last Reply</th>
1007
+ </tr>
1008
+ </thead>
1009
+ <tbody>
1010
+ <tr v-for="t in tickets" :key="t.ref"
1011
+ :style="{
1012
+ borderBottom: '1px solid rgba(255,255,255,0.04)',
1013
+ background: t.checked ? 'rgba(6,182,212,0.06)' : 'transparent',
1014
+ }">
1015
+ <td style="padding:10px 14px;">
1016
+ <div :style="{
1017
+ width: '14px', height: '14px', borderRadius: '3px',
1018
+ border: t.checked ? '1px solid #06b6d4' : '1px solid rgba(255,255,255,0.15)',
1019
+ background: t.checked ? '#06b6d4' : 'transparent',
1020
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
1021
+ }">
1022
+ <svg v-if="t.checked" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
1023
+ </div>
1024
+ </td>
1025
+ <td style="padding:10px 8px; white-space:nowrap;">
1026
+ <div style="display:flex; align-items:center; gap:6px;">
1027
+ <span :style="{
1028
+ width: '7px', height: '7px', borderRadius: '50%',
1029
+ background: t.sla === 'red' ? '#ef4444' : t.sla === 'amber' ? '#f59e0b' : t.sla === 'green' ? '#22c55e' : 'rgba(255,255,255,0.1)',
1030
+ flexShrink: 0,
1031
+ }"></span>
1032
+ <span style="color:#22d3ee; font-weight:500;">{{ t.ref }}</span>
1033
+ </div>
1034
+ </td>
1035
+ <td style="padding:10px 8px; max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#e5e5e5;">{{ t.subject }}</td>
1036
+ <td style="padding:10px 8px; white-space:nowrap;">
1037
+ <div style="display:flex; flex-direction:column;">
1038
+ <span style="color:#d4d4d4; font-size:13px;">{{ t.requester }}</span>
1039
+ <span style="color:#525252; font-size:11px;">{{ t.email }}</span>
1040
+ </div>
1041
+ </td>
1042
+ <td style="padding:10px 8px;"><StatusBadge :status="t.status" /></td>
1043
+ <td style="padding:10px 8px;"><PriorityBadge :priority="t.priority" /></td>
1044
+ <td style="padding:10px 8px; white-space:nowrap;">
1045
+ <span :style="{ color: t.assignee === 'Unassigned' ? '#525252' : '#a3a3a3', fontStyle: t.assignee === 'Unassigned' ? 'italic' : 'normal' }">{{ t.assignee }}</span>
1046
+ </td>
1047
+ <td style="padding:10px 8px; white-space:nowrap;">
1048
+ <div style="display:flex; flex-direction:column;">
1049
+ <span style="color:#a3a3a3; font-size:12px;">{{ t.ago }}</span>
1050
+ <span style="color:#525252; font-size:11px;">{{ t.author }}</span>
1051
+ </div>
1052
+ </td>
1053
+ </tr>
1054
+ </tbody>
1055
+ </table>
1056
+ </div>
1057
+
1058
+ <!-- Pagination -->
1059
+ <div style="display:flex; align-items:center; justify-content:space-between;">
1060
+ <span style="font-size:12px; color:#737373;">Showing 1\u20138 of 142 tickets</span>
1061
+ <div style="display:flex; gap:6px;">
1062
+ <button style="padding:5px 14px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#525252; font-size:12px; cursor:default;">Previous</button>
1063
+ <button style="padding:5px 14px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#e5e5e5; font-size:12px; cursor:pointer;">Next</button>
1064
+ </div>
1065
+ </div>
1066
+
1067
+ <!-- Bulk action bar -->
1068
+ <div style="display:flex; align-items:center; gap:12px; padding:8px 14px; border-radius:10px; background:rgba(6,182,212,0.08); border:1px solid rgba(6,182,212,0.15);">
1069
+ <span style="font-size:12px; color:#22d3ee; font-weight:500;">2 tickets selected</span>
1070
+ <div style="width:1px; height:14px; background:rgba(255,255,255,0.08);"></div>
1071
+ <select style="padding:4px 8px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:11px; outline:none; appearance:auto;">
1072
+ <option>Set Status...</option>
1073
+ <option>Open</option>
1074
+ <option>Pending</option>
1075
+ <option>Solved</option>
1076
+ </select>
1077
+ <select style="padding:4px 8px; border-radius:6px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3; font-size:11px; outline:none; appearance:auto;">
1078
+ <option>Set Priority...</option>
1079
+ <option>Low</option>
1080
+ <option>Medium</option>
1081
+ <option>High</option>
1082
+ <option>Urgent</option>
1083
+ </select>
1084
+ </div>
1085
+
1086
+ </div>
1087
+ </div>
1088
+ </div>
1089
+ `,
1090
+ }),
1091
+ };
1092
+
1093
+ const demoTickets = [
1094
+ {
1095
+ ref: 'ESC-1042',
1096
+ subject: 'Unable to export CSV reports from dashboard',
1097
+ requester: 'Sarah Chen',
1098
+ email: 'sarah@acme.co',
1099
+ status: 'open',
1100
+ priority: 'high',
1101
+ assignee: 'Mike Agent',
1102
+ ago: '12m ago',
1103
+ sla: 'green',
1104
+ },
1105
+ {
1106
+ ref: 'ESC-1041',
1107
+ subject: 'SSO login redirect loop on Firefox',
1108
+ requester: 'James Wilson',
1109
+ email: 'james@corp.io',
1110
+ status: 'pending',
1111
+ priority: 'urgent',
1112
+ assignee: 'Lisa Torres',
1113
+ ago: '34m ago',
1114
+ sla: 'amber',
1115
+ },
1116
+ {
1117
+ ref: 'ESC-1040',
1118
+ subject: 'Custom field values not persisting after save',
1119
+ requester: 'Maria Garcia',
1120
+ email: 'maria@startup.dev',
1121
+ status: 'in_progress',
1122
+ priority: 'medium',
1123
+ assignee: 'Mike Agent',
1124
+ ago: '1h ago',
1125
+ sla: 'green',
1126
+ },
1127
+ {
1128
+ ref: 'ESC-1039',
1129
+ subject: 'Webhook notifications delayed by 15+ minutes',
1130
+ requester: 'Tom Baker',
1131
+ email: 'tom@enterprise.com',
1132
+ status: 'escalated',
1133
+ priority: 'high',
1134
+ assignee: 'Mike Agent',
1135
+ ago: '2h ago',
1136
+ sla: 'red',
1137
+ },
1138
+ {
1139
+ ref: 'ESC-1038',
1140
+ subject: 'Knowledge base search returning no results',
1141
+ requester: 'Priya Patel',
1142
+ email: 'priya@example.com',
1143
+ status: 'solved',
1144
+ priority: 'medium',
1145
+ assignee: 'Lisa Torres',
1146
+ ago: '3h ago',
1147
+ sla: '',
1148
+ },
1149
+ {
1150
+ ref: 'ESC-1037',
1151
+ subject: 'Feature request: Dark mode for customer portal',
1152
+ requester: 'Alex Kim',
1153
+ email: 'alex@design.co',
1154
+ status: 'open',
1155
+ priority: 'low',
1156
+ assignee: 'Unassigned',
1157
+ ago: '5h ago',
1158
+ sla: '',
1159
+ },
1160
+ ];
1161
+
1162
+ /**
1163
+ * Single-page interactive flow used by the README demo GIF.
1164
+ * List ↔ detail transitions happen in one page load so there's no iframe flash.
1165
+ */
1166
+ export const DemoFlow = {
1167
+ parameters: { layout: 'fullscreen' },
1168
+ render: () => ({
1169
+ components: { StatusBadge, PriorityBadge },
1170
+ data: () => ({
1171
+ view: 'list',
1172
+ tickets: demoTickets,
1173
+ selectedTicket: null,
1174
+ messages: [],
1175
+ replyText: '',
1176
+ toast: '',
1177
+ }),
1178
+ methods: {
1179
+ openTicket(t) {
1180
+ this.selectedTicket = t;
1181
+ this.messages = [
1182
+ {
1183
+ id: 1,
1184
+ author: t.requester,
1185
+ initial: t.requester[0],
1186
+ role: 'customer',
1187
+ time: t.ago,
1188
+ text: `Hi team — ${t.subject.charAt(0).toLowerCase() + t.subject.slice(1)}. This is blocking our workflow, any update would be appreciated.`,
1189
+ },
1190
+ {
1191
+ id: 2,
1192
+ author: 'Mike Agent',
1193
+ initial: 'M',
1194
+ role: 'agent',
1195
+ time: '3m ago',
1196
+ text: 'Thanks for the report — I can reproduce this. Engineering has identified the root cause and a patch is already in review.',
1197
+ },
1198
+ ];
1199
+ this.view = 'detail';
1200
+ },
1201
+ backToList() {
1202
+ this.view = 'list';
1203
+ this.replyText = '';
1204
+ },
1205
+ sendReply() {
1206
+ const text = this.replyText.trim();
1207
+ if (!text) return;
1208
+ this.messages.push({
1209
+ id: Date.now(),
1210
+ author: 'Mike Agent',
1211
+ initial: 'M',
1212
+ role: 'agent',
1213
+ time: 'just now',
1214
+ text,
1215
+ });
1216
+ this.replyText = '';
1217
+ this.toast = 'Reply sent';
1218
+ setTimeout(() => {
1219
+ this.toast = '';
1220
+ }, 2800);
1221
+ },
1222
+ slaColor(s) {
1223
+ return s === 'red'
1224
+ ? '#ef4444'
1225
+ : s === 'amber'
1226
+ ? '#f59e0b'
1227
+ : s === 'green'
1228
+ ? '#22c55e'
1229
+ : 'rgba(255,255,255,0.1)';
1230
+ },
1231
+ },
1232
+ template: `
1233
+ <div class="demo-root" data-fullbleed>
1234
+ <nav style="height:48px; border-bottom:1px solid rgba(255,255,255,0.06); background:#0a0a0a; display:flex; align-items:center; justify-content:space-between; padding:0 24px; flex-shrink:0;">
1235
+ <div style="display:flex; align-items:center; gap:10px;">
1236
+ <div style="width:30px; height:30px; border-radius:8px; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center;">
1237
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
1238
+ <defs><linearGradient id="esc-rb-demo" 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>
1239
+ <g transform="translate(12,12) scale(1.35) translate(-12,-12)"><polyline points="17 11 12 6 7 11" stroke="url(#esc-rb-demo)"/><polyline points="17 18 12 13 7 18" stroke="url(#esc-rb-demo)"/></g>
1240
+ </svg>
1241
+ </div>
1242
+ <span style="font-size:13px; font-weight:700; letter-spacing:0.02em;">Escalated</span>
1243
+ </div>
1244
+ <div style="display:flex; align-items:center; gap:4px;">
1245
+ <span style="padding:5px 12px; border-radius:8px; font-size:13px; font-weight:500; color:#737373; cursor:pointer;">Dashboard</span>
1246
+ <span style="background:#1f2937; padding:5px 12px; border-radius:8px; font-size:13px; font-weight:600; color:#fff; cursor:pointer;" @click="backToList">Tickets</span>
1247
+ </div>
1248
+ <div style="display:flex; align-items:center; gap:12px;">
1249
+ <span style="font-size:13px; color:#737373;">Admin</span>
1250
+ <div style="width:1px; height:16px; background:rgba(255,255,255,0.08);"></div>
1251
+ <div style="display:flex; align-items:center; gap:8px;">
1252
+ <div style="width:26px; height:26px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:600; color:#a3a3a3;">M</div>
1253
+ <span style="font-size:13px; color:#a3a3a3;">Mike Agent</span>
1254
+ </div>
1255
+ </div>
1256
+ </nav>
1257
+
1258
+ <transition name="demo-fade" mode="out-in">
1259
+ <div v-if="view === 'list'" key="list" style="flex:1; display:flex; justify-content:center; padding:20px 24px; overflow-y:auto;">
1260
+ <div style="max-width:1100px; width:100%; display:flex; flex-direction:column; gap:16px;">
1261
+ <div style="display:flex; justify-content:space-between; align-items:center;">
1262
+ <h1 style="font-size:20px; font-weight:600; margin:0; color:#f5f5f5;">All Tickets</h1>
1263
+ <span style="font-size:12px; color:#737373;">142 tickets · 18 unassigned</span>
1264
+ </div>
1265
+ <div style="display:flex; gap:8px; flex-wrap:wrap;">
1266
+ <span style="padding:4px 14px; border-radius:20px; font-size:12px; font-weight:500; cursor:pointer; border:1px solid rgba(6,182,212,0.4); background:rgba(6,182,212,0.15); color:#22d3ee;">My Tickets</span>
1267
+ <span style="padding:4px 14px; border-radius:20px; font-size:12px; font-weight:500; cursor:pointer; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3;">Unassigned</span>
1268
+ <span style="padding:4px 14px; border-radius:20px; font-size:12px; font-weight:500; cursor:pointer; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3;">Urgent+</span>
1269
+ <span style="padding:4px 14px; border-radius:20px; font-size:12px; font-weight:500; cursor:pointer; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3;">SLA Breaching</span>
1270
+ <span style="padding:4px 14px; border-radius:20px; font-size:12px; font-weight:500; cursor:pointer; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#a3a3a3;">Following</span>
1271
+ </div>
1272
+ <div style="position:relative; width:100%;">
1273
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#a3a3a3" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="position:absolute; left:12px; top:50%; transform:translateY(-50%); pointer-events:none;"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m20 20-4.85-4.85"/></svg>
1274
+ <input type="text" placeholder="Search tickets..." style="width:100%; padding:8px 12px 8px 38px; border-radius:8px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:#fff; font-size:13px; outline:none; box-sizing:border-box;" />
1275
+ </div>
1276
+ <div style="border-radius:12px; border:1px solid rgba(255,255,255,0.06); background:rgba(23,23,23,0.6); overflow:hidden;">
1277
+ <table style="width:100%; border-collapse:collapse; font-size:13px;">
1278
+ <thead>
1279
+ <tr style="border-bottom:1px solid rgba(255,255,255,0.06);">
1280
+ <th style="padding:10px 14px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Reference</th>
1281
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Subject</th>
1282
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Requester</th>
1283
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Status</th>
1284
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Priority</th>
1285
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Assignee</th>
1286
+ <th style="padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:#737373; text-transform:uppercase; letter-spacing:0.05em;">Last Reply</th>
1287
+ </tr>
1288
+ </thead>
1289
+ <tbody>
1290
+ <tr v-for="t in tickets" :key="t.ref"
1291
+ class="demo-ticket-row"
1292
+ data-testid="demo-ticket-row"
1293
+ :data-ref="t.ref"
1294
+ @click="openTicket(t)"
1295
+ style="border-bottom:1px solid rgba(255,255,255,0.04);">
1296
+ <td style="padding:10px 14px; white-space:nowrap;">
1297
+ <div style="display:flex; align-items:center; gap:6px;">
1298
+ <span :style="{ width: '7px', height: '7px', borderRadius: '50%', background: slaColor(t.sla), flexShrink: 0 }"></span>
1299
+ <span style="color:#22d3ee; font-weight:500;">{{ t.ref }}</span>
1300
+ </div>
1301
+ </td>
1302
+ <td style="padding:10px 8px; max-width:280px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#e5e5e5;">{{ t.subject }}</td>
1303
+ <td style="padding:10px 8px; white-space:nowrap;">
1304
+ <div style="display:flex; flex-direction:column;">
1305
+ <span style="color:#d4d4d4; font-size:13px;">{{ t.requester }}</span>
1306
+ <span style="color:#525252; font-size:11px;">{{ t.email }}</span>
1307
+ </div>
1308
+ </td>
1309
+ <td style="padding:10px 8px;"><StatusBadge :status="t.status" /></td>
1310
+ <td style="padding:10px 8px;"><PriorityBadge :priority="t.priority" /></td>
1311
+ <td style="padding:10px 8px; white-space:nowrap;">
1312
+ <span :style="{ color: t.assignee === 'Unassigned' ? '#525252' : '#a3a3a3', fontStyle: t.assignee === 'Unassigned' ? 'italic' : 'normal' }">{{ t.assignee }}</span>
1313
+ </td>
1314
+ <td style="padding:10px 8px; white-space:nowrap; color:#a3a3a3; font-size:12px;">{{ t.ago }}</td>
1315
+ </tr>
1316
+ </tbody>
1317
+ </table>
1318
+ </div>
1319
+ </div>
1320
+ </div>
1321
+
1322
+ <div v-else key="detail" style="flex:1; display:flex; min-height:0;">
1323
+ <div style="flex:7; display:flex; flex-direction:column; border-right:1px solid rgba(255,255,255,0.06); min-width:0;">
1324
+ <div style="padding:14px 24px; border-bottom:1px solid rgba(255,255,255,0.06); flex-shrink:0;">
1325
+ <div style="display:flex; align-items:center; gap:10px; margin-bottom:6px;">
1326
+ <span @click="backToList" style="font-size:12px; color:#737373; cursor:pointer; display:inline-flex; align-items:center; gap:4px;">
1327
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
1328
+ Tickets
1329
+ </span>
1330
+ <span style="color:#404040;">·</span>
1331
+ <span style="font-size:13px; color:#22d3ee; font-weight:500;">{{ selectedTicket?.ref }}</span>
1332
+ <StatusBadge :status="selectedTicket?.status" />
1333
+ <PriorityBadge :priority="selectedTicket?.priority" />
1334
+ </div>
1335
+ <h1 style="font-size:16px; font-weight:600; margin:0; color:#f5f5f5;">{{ selectedTicket?.subject }}</h1>
1336
+ </div>
1337
+
1338
+ <div style="flex:1; overflow-y:auto; padding:16px 24px;">
1339
+ <transition-group name="demo-msg" tag="div" style="display:flex; flex-direction:column; gap:14px;">
1340
+ <div v-for="msg in messages" :key="msg.id" style="display:flex; gap:12px;">
1341
+ <div :style="{ width: '32px', height: '32px', borderRadius: '8px', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', fontWeight: '600', background: msg.role === 'agent' ? 'rgba(59,130,246,0.15)' : 'rgba(255,255,255,0.08)', color: msg.role === 'agent' ? '#60a5fa' : '#a3a3a3' }">{{ msg.initial }}</div>
1342
+ <div style="flex:1; min-width:0;">
1343
+ <div style="display:flex; align-items:center; gap:8px; margin-bottom:4px;">
1344
+ <span :style="{ fontSize: '13px', fontWeight: '600', color: msg.role === 'agent' ? '#60a5fa' : '#e5e5e5' }">{{ msg.author }}</span>
1345
+ <span v-if="msg.role === 'agent'" style="font-size:10px; background:rgba(59,130,246,0.12); color:#60a5fa; padding:1px 6px; border-radius:4px; font-weight:500;">Agent</span>
1346
+ <span style="font-size:11px; color:#525252;">{{ msg.time }}</span>
1347
+ </div>
1348
+ <div style="font-size:13px; line-height:1.6; color:#d4d4d4; background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.04); border-radius:10px; padding:10px 14px;">{{ msg.text }}</div>
1349
+ </div>
1350
+ </div>
1351
+ </transition-group>
1352
+ </div>
1353
+
1354
+ <div style="border-top:1px solid rgba(255,255,255,0.06); padding:12px 24px; flex-shrink:0;">
1355
+ <div style="display:flex; gap:0; margin-bottom:8px;">
1356
+ <button style="padding:5px 14px; font-size:12px; font-weight:600; border:none; cursor:pointer; border-radius:6px 0 0 6px; background:rgba(59,130,246,0.15); color:#60a5fa;">Reply</button>
1357
+ <button style="padding:5px 14px; font-size:12px; font-weight:600; border:none; cursor:pointer; border-radius:0 6px 6px 0; background:rgba(255,255,255,0.05); color:#737373;">Internal Note</button>
1358
+ </div>
1359
+ <textarea
1360
+ v-model="replyText"
1361
+ data-testid="demo-reply-textarea"
1362
+ placeholder="Type your reply..."
1363
+ style="width:100%; min-height:68px; background:rgba(255,255,255,0.04); border:1px solid rgba(255,255,255,0.08); border-radius:8px; padding:10px 14px; font-size:13px; color:#e5e5e5; resize:none; font-family:inherit; box-sizing:border-box; outline:none;"
1364
+ ></textarea>
1365
+ <div style="display:flex; justify-content:flex-end; margin-top:8px;">
1366
+ <button
1367
+ data-testid="demo-send-reply"
1368
+ @click="sendReply"
1369
+ :disabled="!replyText.trim()"
1370
+ :style="{ padding: '6px 16px', borderRadius: '8px', fontSize: '12px', fontWeight: '600', border: 'none', cursor: replyText.trim() ? 'pointer' : 'not-allowed', background: '#3b82f6', color: '#fff', opacity: replyText.trim() ? '1' : '0.5', transition: 'opacity 150ms ease' }"
1371
+ >Send Reply</button>
1372
+ </div>
1373
+ </div>
1374
+ </div>
1375
+
1376
+ <div style="flex:3; padding:16px; overflow-y:auto; background:rgba(10,10,10,0.5); min-width:0;">
1377
+ <div style="margin-bottom:20px;">
1378
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Assignee</div>
1379
+ <div style="display:flex; align-items:center; gap:8px;">
1380
+ <div style="width:28px; height:28px; border-radius:6px; background:rgba(59,130,246,0.15); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#60a5fa;">M</div>
1381
+ <div>
1382
+ <div style="font-size:13px; font-weight:500; color:#e5e5e5;">{{ selectedTicket?.assignee }}</div>
1383
+ <div style="font-size:11px; color:#525252;">Support Team</div>
1384
+ </div>
1385
+ </div>
1386
+ </div>
1387
+ <div style="margin-bottom:20px;">
1388
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Department</div>
1389
+ <div style="font-size:13px; color:#a3a3a3;">Technical Support</div>
1390
+ </div>
1391
+ <div style="margin-bottom:20px;">
1392
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">SLA</div>
1393
+ <div style="border:1px solid rgba(16,185,129,0.2); background:rgba(16,185,129,0.08); border-radius:8px; padding:8px 12px;">
1394
+ <div style="font-size:12px; font-weight:500; color:#34d399;">Next Response Due</div>
1395
+ <div style="font-size:11px; color:#34d399;">3h 42m remaining</div>
1396
+ </div>
1397
+ </div>
1398
+ <div>
1399
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#525252; margin-bottom:6px;">Requester</div>
1400
+ <div style="background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.06); border-radius:8px; padding:10px 12px;">
1401
+ <div style="display:flex; align-items:center; gap:8px;">
1402
+ <div style="width:28px; height:28px; border-radius:6px; background:rgba(255,255,255,0.08); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#a3a3a3;">{{ selectedTicket?.requester?.[0] }}</div>
1403
+ <div>
1404
+ <div style="font-size:13px; font-weight:500; color:#e5e5e5;">{{ selectedTicket?.requester }}</div>
1405
+ <div style="font-size:11px; color:#525252;">{{ selectedTicket?.email }}</div>
1406
+ </div>
1407
+ </div>
1408
+ </div>
1409
+ </div>
1410
+ </div>
1411
+ </div>
1412
+ </transition>
1413
+
1414
+ <transition name="demo-toast">
1415
+ <div v-if="toast" style="position:fixed; bottom:20px; right:20px; background:rgba(16,185,129,0.15); border:1px solid rgba(16,185,129,0.3); color:#34d399; padding:10px 16px; border-radius:10px; font-size:13px; font-weight:500; display:flex; align-items:center; gap:8px; backdrop-filter: blur(8px);">
1416
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
1417
+ {{ toast }}
1418
+ </div>
1419
+ </transition>
1420
+ </div>
1421
+ `,
1422
+ }),
1423
+ };