@escalated-dev/escalated 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +94 -2
  2. package/package.json +31 -2
  3. package/src/components/ActivityTimeline.stories.js +55 -0
  4. package/src/components/ActivityTimeline.vue +24 -7
  5. package/src/components/AdminDashboard.stories.js +1095 -0
  6. package/src/components/AgentLoadIndicator.stories.js +37 -0
  7. package/src/components/AgentLoadIndicator.vue +49 -0
  8. package/src/components/AgentPerformanceCard.vue +44 -0
  9. package/src/components/ArticleEditor.vue +82 -0
  10. package/src/components/AssigneeSelect.vue +19 -5
  11. package/src/components/AttachmentList.vue +53 -37
  12. package/src/components/AuditLogEntry.vue +95 -0
  13. package/src/components/BulkActionBar.vue +99 -30
  14. package/src/components/CategoryTree.vue +89 -0
  15. package/src/components/ChartWidget.vue +46 -0
  16. package/src/components/CollisionWarning.stories.js +30 -0
  17. package/src/components/CollisionWarning.vue +68 -0
  18. package/src/components/ConditionalFieldRules.vue +135 -0
  19. package/src/components/ContextPanel.vue +66 -0
  20. package/src/components/ContextPanelSection.vue +65 -0
  21. package/src/components/CustomFieldRenderer.vue +159 -0
  22. package/src/components/EscalatedLayout.vue +400 -69
  23. package/src/components/FileDropzone.vue +26 -10
  24. package/src/components/FollowButton.stories.js +30 -0
  25. package/src/components/FollowButton.vue +53 -28
  26. package/src/components/KeyboardShortcutHelp.vue +129 -39
  27. package/src/components/KnowledgePanel.vue +122 -0
  28. package/src/components/KpiCard.stories.js +32 -0
  29. package/src/components/KpiCard.vue +88 -0
  30. package/src/components/MacroDropdown.vue +95 -35
  31. package/src/components/PermissionMatrix.vue +107 -0
  32. package/src/components/PinnedNotes.vue +70 -31
  33. package/src/components/PresenceIndicator.vue +93 -27
  34. package/src/components/PriorityBadge.stories.js +33 -0
  35. package/src/components/PriorityBadge.vue +9 -2
  36. package/src/components/QuickFilters.vue +65 -49
  37. package/src/components/ReplyComposer.vue +144 -30
  38. package/src/components/ReplyThread.vue +72 -20
  39. package/src/components/SatisfactionRating.vue +98 -46
  40. package/src/components/ScheduleEditor.vue +104 -0
  41. package/src/components/SideConversation.vue +165 -0
  42. package/src/components/SideConversationList.vue +147 -0
  43. package/src/components/SkillTagManager.vue +79 -0
  44. package/src/components/SlaComplianceChart.vue +53 -0
  45. package/src/components/SlaTimer.stories.js +35 -0
  46. package/src/components/SlaTimer.vue +17 -4
  47. package/src/components/StatsCard.stories.js +24 -0
  48. package/src/components/StatsCard.vue +58 -46
  49. package/src/components/StatusBadge.stories.js +53 -0
  50. package/src/components/StatusBadge.vue +36 -4
  51. package/src/components/TagSelect.vue +43 -12
  52. package/src/components/TicketFilters.vue +225 -69
  53. package/src/components/TicketLinkPanel.vue +193 -0
  54. package/src/components/TicketList.vue +594 -193
  55. package/src/components/TicketMergeDialog.vue +157 -0
  56. package/src/components/TicketSidebar.vue +167 -99
  57. package/src/components/TicketTypeSelector.vue +21 -0
  58. package/src/components/TotpInput.stories.js +9 -0
  59. package/src/components/TotpInput.vue +81 -0
  60. package/src/components/TwoFactorSetup.vue +113 -0
  61. package/src/composables/useDebouncedSearch.js +38 -0
  62. package/src/composables/usePluginExtensions.js +95 -3
  63. package/src/composables/usePluginHooks.js +65 -0
  64. package/src/defineEscalatedPlugin.js +34 -8
  65. package/src/index.js +72 -35
  66. package/src/locales/en.json +25 -0
  67. package/src/pages/Admin/ApiTokens/Index.vue +231 -67
  68. package/src/pages/Admin/AuditLog/Index.vue +154 -0
  69. package/src/pages/Admin/Automations/Form.vue +184 -0
  70. package/src/pages/Admin/Automations/Index.vue +129 -0
  71. package/src/pages/Admin/BusinessHours/Form.vue +172 -0
  72. package/src/pages/Admin/BusinessHours/Index.vue +113 -0
  73. package/src/pages/Admin/CannedResponses/Index.vue +127 -70
  74. package/src/pages/Admin/Capacity/Index.vue +134 -0
  75. package/src/pages/Admin/CustomFields/Form.vue +195 -0
  76. package/src/pages/Admin/CustomFields/Index.vue +134 -0
  77. package/src/pages/Admin/CustomObjects/Form.vue +202 -0
  78. package/src/pages/Admin/CustomObjects/Index.vue +127 -0
  79. package/src/pages/Admin/CustomObjects/Records.vue +228 -0
  80. package/src/pages/Admin/Departments/Form.vue +75 -51
  81. package/src/pages/Admin/Departments/Index.vue +109 -57
  82. package/src/pages/Admin/EscalationRules/Form.vue +157 -96
  83. package/src/pages/Admin/EscalationRules/Index.vue +178 -57
  84. package/src/pages/Admin/Import/Connect.vue +96 -0
  85. package/src/pages/Admin/Import/Index.vue +102 -0
  86. package/src/pages/Admin/Import/Mapping.vue +110 -0
  87. package/src/pages/Admin/Import/Progress.vue +153 -0
  88. package/src/pages/Admin/Import/Review.vue +76 -0
  89. package/src/pages/Admin/KnowledgeBase/Articles/Form.vue +142 -0
  90. package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +195 -0
  91. package/src/pages/Admin/KnowledgeBase/Categories/Index.vue +193 -0
  92. package/src/pages/Admin/Macros/Index.vue +250 -89
  93. package/src/pages/Admin/Plugins/Index.vue +205 -75
  94. package/src/pages/Admin/Reports/AgentMetrics.vue +115 -0
  95. package/src/pages/Admin/Reports/CsatReport.vue +127 -0
  96. package/src/pages/Admin/Reports/Dashboard.vue +211 -0
  97. package/src/pages/Admin/Reports/SlaReport.vue +166 -0
  98. package/src/pages/Admin/Reports.vue +139 -34
  99. package/src/pages/Admin/Roles/Form.vue +71 -0
  100. package/src/pages/Admin/Roles/Index.vue +122 -0
  101. package/src/pages/Admin/Settings/CsatSettings.vue +165 -0
  102. package/src/pages/Admin/Settings/DataRetention.vue +182 -0
  103. package/src/pages/Admin/Settings/EmailSettings.vue +243 -0
  104. package/src/pages/Admin/Settings/Sandbox.vue +135 -0
  105. package/src/pages/Admin/Settings/SsoSettings.vue +218 -0
  106. package/src/pages/Admin/Settings/TwoFactor.vue +110 -0
  107. package/src/pages/Admin/Settings.vue +468 -260
  108. package/src/pages/Admin/Skills/Form.vue +53 -0
  109. package/src/pages/Admin/Skills/Index.vue +94 -0
  110. package/src/pages/Admin/SlaPolicies/Form.vue +118 -75
  111. package/src/pages/Admin/SlaPolicies/Index.vue +120 -59
  112. package/src/pages/Admin/Statuses/Form.vue +114 -0
  113. package/src/pages/Admin/Statuses/Index.vue +153 -0
  114. package/src/pages/Admin/Tags/Index.vue +191 -105
  115. package/src/pages/Admin/Tickets/Index.vue +91 -39
  116. package/src/pages/Admin/Tickets/Show.vue +101 -37
  117. package/src/pages/Admin/Webhooks/DeliveryLog.vue +120 -0
  118. package/src/pages/Admin/Webhooks/Form.vue +110 -0
  119. package/src/pages/Admin/Webhooks/Index.vue +136 -0
  120. package/src/pages/Agent/Dashboard.vue +311 -186
  121. package/src/pages/Agent/TicketIndex.vue +88 -38
  122. package/src/pages/Agent/TicketShow.vue +258 -51
  123. package/src/pages/Customer/Create.vue +36 -9
  124. package/src/pages/Customer/Index.vue +31 -24
  125. package/src/pages/Customer/KnowledgeBase/Article.vue +127 -0
  126. package/src/pages/Customer/KnowledgeBase/Index.vue +151 -0
  127. package/src/plugin.js +86 -1
  128. package/src/utils/constants.js +30 -0
  129. package/src/utils/formatting.js +35 -0
  130. package/src/pages/Admin/Plugins.vue +0 -129
package/README.md CHANGED
@@ -3,6 +3,11 @@
3
3
  Escalated
4
4
  </h1>
5
5
 
6
+ [![Tests](https://github.com/escalated-dev/escalated/actions/workflows/run-tests.yml/badge.svg)](https://github.com/escalated-dev/escalated/actions/workflows/run-tests.yml)
7
+ [![npm](https://img.shields.io/npm/v/@escalated-dev/escalated)](https://www.npmjs.com/package/@escalated-dev/escalated)
8
+ [![Vue 3](https://img.shields.io/badge/vue-3.x-4FC08D?logo=vue.js&logoColor=white)](https://vuejs.org/)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+
6
11
  Escalated is an embeddable support ticket system with SLA tracking, escalation rules, agent workflows, and a customer portal. This repo contains all the shared frontend assets (Vue 3 + Inertia.js) used across every supported backend framework.
7
12
 
8
13
  👉 **Learn more, view demos, and compare Cloud vs Self-Hosted options at** **[https://escalated.dev](https://escalated.dev)**
@@ -19,6 +24,10 @@ Pick your framework:
19
24
  | **Rails** | [escalated-dev/escalated-rails](https://github.com/escalated-dev/escalated-rails) | `gem "escalated"` |
20
25
  | **Django** | [escalated-dev/escalated-django](https://github.com/escalated-dev/escalated-django) | `pip install escalated-django` |
21
26
  | **AdonisJS** | [escalated-dev/escalated-adonis](https://github.com/escalated-dev/escalated-adonis) | `npm install @escalated-dev/escalated-adonis` |
27
+ | **WordPress** | [escalated-dev/escalated-wordpress](https://github.com/escalated-dev/escalated-wordpress) | [Download escalated.zip](https://github.com/escalated-dev/escalated-wordpress/releases/latest) |
28
+ | **Filament** | [escalated-dev/escalated-filament](https://github.com/escalated-dev/escalated-filament) | `composer require escalated-dev/escalated-filament` |
29
+ | **React Native** | [escalated-dev/escalated-react-native](https://github.com/escalated-dev/escalated-react-native) | `npm install @escalated-dev/escalated-react-native` |
30
+ | **Flutter** | [escalated-dev/escalated-flutter](https://github.com/escalated-dev/escalated-flutter) | See [pubspec.yaml setup](https://github.com/escalated-dev/escalated-flutter#quick-start) |
22
31
 
23
32
  Each backend repo has full setup instructions — install command, migrations, config, and frontend integration.
24
33
 
@@ -127,12 +136,31 @@ All the Vue 3 + Inertia.js components that power the Escalated UI. These are ide
127
136
 
128
137
  ## 📸 Screenshots
129
138
 
139
+ > Screenshots are auto-generated from Storybook via the [component-screenshots](.github/workflows/screenshots.yml) workflow.
140
+
141
+ <p align="center">
142
+ <strong>Admin Panel (Dark)</strong><br/>
143
+ <img src="docs/assets/escalated_admin_1.png" alt="Escalated Admin Panel — dark mode with sidebar navigation, KPI cards, stats, and ticket list" width="800" />
144
+ </p>
145
+
130
146
  <p align="center">
131
- <img src="docs/assets/escalated_admin_1.png" alt="Escalated Admin Screenshot 1" width="800" />
147
+ <strong>Admin Panel (Light)</strong><br/>
148
+ <img src="docs/assets/escalated_admin_3.png" alt="Escalated Admin Panel — light mode with sidebar navigation, KPI cards, stats, and ticket list" width="800" />
132
149
  </p>
133
150
 
134
151
  <p align="center">
135
- <img src="docs/assets/escalated_admin_2.png" alt="Escalated Admin Screenshot 2" width="800" />
152
+ <strong>Ticket Queue</strong><br/>
153
+ <img src="docs/assets/escalated_ticket_list.png" alt="Escalated Ticket Queue — agent ticket list with filters, search, bulk actions, and SLA indicators" width="800" />
154
+ </p>
155
+
156
+ <p align="center">
157
+ <strong>Agent Panel</strong><br/>
158
+ <img src="docs/assets/escalated_admin_2.png" alt="Escalated Agent Panel — top navigation, stats, and assigned ticket queue" width="800" />
159
+ </p>
160
+
161
+ <p align="center">
162
+ <strong>Ticket Detail View</strong><br/>
163
+ <img src="docs/assets/escalated_ticket_view.png" alt="Escalated Ticket Detail View — conversation thread, reply composer, and ticket sidebar with SLA timer" width="800" />
136
164
  </p>
137
165
 
138
166
  ### Pages
@@ -197,6 +225,70 @@ Reusable building blocks used across the pages above.
197
225
  |--------|-------------|
198
226
  | `EscalatedPlugin` | Vue plugin for layout injection and CSS theming |
199
227
 
228
+ ## Plugin Development
229
+
230
+ Escalated supports framework-agnostic plugins built with the [Plugin SDK](https://github.com/escalated-dev/escalated-plugin-sdk). Plugins are written once in TypeScript and work across all Escalated backends.
231
+
232
+ ### How the Frontend Plugin System Works
233
+
234
+ The frontend uses `defineEscalatedPlugin()` to register Vue components — custom admin pages, ticket sidebar widgets, or dashboard panels — that are mounted automatically when the plugin is active.
235
+
236
+ ```typescript
237
+ import { defineEscalatedPlugin } from '@escalated-dev/escalated'
238
+ import MySettingsPage from './MySettingsPage.vue'
239
+
240
+ export default defineEscalatedPlugin({
241
+ name: 'my-plugin',
242
+ pages: {
243
+ 'admin/my-plugin/settings': MySettingsPage,
244
+ },
245
+ })
246
+ ```
247
+
248
+ ### How It Connects to the Backend
249
+
250
+ The backend uses `definePlugin()` from the [Plugin SDK](https://github.com/escalated-dev/escalated-plugin-sdk) to handle TypeScript business logic — subscribing to ticket lifecycle hooks, exposing API endpoints, and persisting data. The frontend and backend entries work together as a single npm package.
251
+
252
+ ```typescript
253
+ // backend entry (index.ts)
254
+ import { definePlugin } from '@escalated-dev/plugin-sdk'
255
+
256
+ export default definePlugin({
257
+ name: 'my-plugin',
258
+ version: '1.0.0',
259
+ actions: {
260
+ 'ticket.created': async (event, ctx) => {
261
+ ctx.log.info('New ticket!', event)
262
+ },
263
+ },
264
+ })
265
+ ```
266
+
267
+ ### Quick Example: Both Entry Points
268
+
269
+ A published plugin package typically exports both:
270
+
271
+ ```
272
+ my-plugin/
273
+ index.ts ← backend: definePlugin() for TypeScript logic
274
+ frontend.ts ← frontend: defineEscalatedPlugin() for Vue components
275
+ ```
276
+
277
+ The backend framework (Laravel, Rails, Django, AdonisJS) loads `index.ts` via the [Plugin Runtime](https://github.com/escalated-dev/escalated-plugin-runtime). The Vue app imports `frontend.ts` and registers it with `app.use()`.
278
+
279
+ ### Installing Plugins
280
+
281
+ ```bash
282
+ npm install @escalated-dev/plugin-slack
283
+ npm install @escalated-dev/plugin-jira
284
+ ```
285
+
286
+ ### Resources
287
+
288
+ - [Plugin SDK](https://github.com/escalated-dev/escalated-plugin-sdk) — TypeScript SDK for building plugins
289
+ - [Plugin Runtime](https://github.com/escalated-dev/escalated-plugin-runtime) — Runtime host for plugins
290
+ - [Plugin Development Guide](https://github.com/escalated-dev/escalated-docs) — Full documentation
291
+
200
292
  ## For Package Maintainers
201
293
 
202
294
  If you're building a new backend integration, this package is available on npm:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@escalated-dev/escalated",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Vue 3 + Inertia.js UI components for Escalated — the embeddable support ticket system",
5
5
  "author": "Escalated Dev <hello@escalated.dev>",
6
6
  "license": "MIT",
@@ -33,13 +33,42 @@
33
33
  "vue": "^3.3.0"
34
34
  },
35
35
  "scripts": {
36
- "test": "vitest run"
36
+ "test": "vitest run",
37
+ "lint": "eslint src/",
38
+ "lint:fix": "eslint src/ --fix",
39
+ "format": "prettier --write src/",
40
+ "format:check": "prettier --check src/",
41
+ "prepare": "husky",
42
+ "storybook": "storybook dev -p 6006",
43
+ "build-storybook": "storybook build",
44
+ "screenshot": "npx playwright test --config=playwright-screenshots.config.js"
45
+ },
46
+ "lint-staged": {
47
+ "*.{js,vue}": [
48
+ "eslint --fix",
49
+ "prettier --write"
50
+ ]
37
51
  },
38
52
  "devDependencies": {
53
+ "@eslint/js": "^10.0.1",
39
54
  "@inertiajs/vue3": "^2.0.0",
55
+ "@playwright/test": "^1.50.0",
56
+ "@storybook/addon-a11y": "^10.2.12",
57
+ "@storybook/addon-docs": "^10.2.12",
58
+ "@storybook/vue3-vite": "^10.2.12",
40
59
  "@vitejs/plugin-vue": "^5.0.0",
41
60
  "@vue/test-utils": "^2.4.0",
61
+ "autoprefixer": "^10.4.20",
62
+ "eslint": "^10.0.1",
63
+ "eslint-plugin-storybook": "^10.2.12",
64
+ "eslint-plugin-vue": "^10.8.0",
42
65
  "happy-dom": "^15.0.0",
66
+ "husky": "^9.1.7",
67
+ "lint-staged": "^16.2.7",
68
+ "postcss": "^8.5.0",
69
+ "prettier": "^3.8.1",
70
+ "storybook": "^10.2.12",
71
+ "tailwindcss": "^3.4.0",
43
72
  "vitest": "^2.0.0",
44
73
  "vue": "^3.5.0"
45
74
  }
@@ -0,0 +1,55 @@
1
+ import ActivityTimeline from './ActivityTimeline.vue';
2
+
3
+ export default {
4
+ title: 'Components/ActivityTimeline',
5
+ component: ActivityTimeline,
6
+ };
7
+
8
+ const sampleActivities = [
9
+ {
10
+ id: 1,
11
+ type: 'status_changed',
12
+ causer: { name: 'Alice Chen' },
13
+ properties: { from: 'open', to: 'in_progress' },
14
+ created_at: new Date(Date.now() - 300000).toISOString(),
15
+ },
16
+ {
17
+ id: 2,
18
+ type: 'assigned',
19
+ causer: { name: 'Bob Smith' },
20
+ properties: { agent_name: 'Alice Chen' },
21
+ created_at: new Date(Date.now() - 600000).toISOString(),
22
+ },
23
+ {
24
+ id: 3,
25
+ type: 'priority_changed',
26
+ causer: { name: 'Alice Chen' },
27
+ properties: { from: 'medium', to: 'high' },
28
+ created_at: new Date(Date.now() - 1200000).toISOString(),
29
+ },
30
+ {
31
+ id: 4,
32
+ type: 'tag_added',
33
+ causer: { name: 'Alice Chen' },
34
+ properties: { tag: 'billing' },
35
+ created_at: new Date(Date.now() - 1800000).toISOString(),
36
+ },
37
+ {
38
+ id: 5,
39
+ type: 'replied',
40
+ causer: { name: 'Alice Chen' },
41
+ properties: {},
42
+ created_at: new Date(Date.now() - 2400000).toISOString(),
43
+ },
44
+ {
45
+ id: 6,
46
+ type: 'sla_breached',
47
+ causer: null,
48
+ properties: {},
49
+ created_at: new Date(Date.now() - 3600000).toISOString(),
50
+ },
51
+ ];
52
+
53
+ export const Default = { args: { activities: sampleActivities } };
54
+ export const Empty = { args: { activities: [] } };
55
+ export const SingleItem = { args: { activities: [sampleActivities[0]] } };
@@ -5,7 +5,10 @@ defineProps({
5
5
  activities: { type: Array, required: true },
6
6
  });
7
7
 
8
- const dark = inject('esc-dark', computed(() => false));
8
+ const dark = inject(
9
+ 'esc-dark',
10
+ computed(() => false),
11
+ );
9
12
 
10
13
  const typeLabels = {
11
14
  status_changed: 'changed status',
@@ -53,14 +56,28 @@ function describeActivity(activity) {
53
56
  </script>
54
57
 
55
58
  <template>
56
- <div class="space-y-3">
57
- <div v-for="activity in activities" :key="activity.id" class="flex gap-3 text-sm">
58
- <div :class="['mt-1 h-2 w-2 flex-shrink-0 rounded-full', dark ? 'bg-neutral-600' : 'bg-gray-400']"></div>
59
+ <div class="space-y-3" role="feed">
60
+ <div v-for="activity in activities" :key="activity.id" role="article" class="flex gap-3 text-sm">
61
+ <div
62
+ :class="[
63
+ 'mt-1 h-2 w-2 flex-shrink-0 rounded-full',
64
+ dark ? 'bg-[var(--esc-panel-text-muted)]' : 'bg-gray-400',
65
+ ]"
66
+ ></div>
59
67
  <div class="flex-1">
60
- <p :class="dark ? 'text-neutral-300' : 'text-gray-700'">{{ describeActivity(activity) }}</p>
61
- <p :class="['text-xs', dark ? 'text-neutral-600' : 'text-gray-400']">{{ formatDate(activity.created_at) }}</p>
68
+ <p :class="dark ? 'text-[var(--esc-panel-text-secondary)]' : 'text-gray-700'">
69
+ {{ describeActivity(activity) }}
70
+ </p>
71
+ <p :class="['text-xs', dark ? 'text-[var(--esc-panel-text-muted)]' : 'text-gray-400']">
72
+ {{ formatDate(activity.created_at) }}
73
+ </p>
62
74
  </div>
63
75
  </div>
64
- <div v-if="!activities?.length" :class="['py-4 text-center text-sm', dark ? 'text-neutral-600' : 'text-gray-500']">No activity yet.</div>
76
+ <div
77
+ v-if="!activities?.length"
78
+ :class="['py-4 text-center text-sm', dark ? 'text-[var(--esc-panel-text-muted)]' : 'text-gray-500']"
79
+ >
80
+ No activity yet.
81
+ </div>
65
82
  </div>
66
83
  </template>