@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,53 +1,193 @@
1
- <script setup>
2
- import EscalatedLayout from '../../../components/EscalatedLayout.vue';
3
- import { useForm } from '@inertiajs/vue3';
4
-
5
- const props = defineProps({
6
- skill: { type: Object, default: null },
7
- });
8
-
9
- const form = useForm({
10
- name: props.skill?.name || '',
11
- });
12
-
13
- function submit() {
14
- if (props.skill) {
15
- form.put(route('escalated.admin.skills.update', props.skill.id));
16
- } else {
17
- form.post(route('escalated.admin.skills.store'));
18
- }
19
- }
20
- </script>
21
-
22
- <template>
23
- <EscalatedLayout :title="skill ? 'Edit Skill' : 'Create Skill'">
24
- <form class="max-w-lg space-y-4" @submit.prevent="submit">
25
- <div>
26
- <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Name</label>
27
- <input
28
- v-model="form.name"
29
- type="text"
30
- required
31
- class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
32
- />
33
- <p v-if="form.errors.name" class="mt-1 text-xs text-rose-400">{{ form.errors.name }}</p>
34
- </div>
35
-
36
- <div class="flex gap-3">
37
- <button
38
- type="submit"
39
- :disabled="form.processing"
40
- class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-2 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)] disabled:opacity-50"
41
- >
42
- {{ skill ? 'Update' : 'Create' }}
43
- </button>
44
- <a
45
- :href="route('escalated.admin.skills.index')"
46
- class="rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-hover)] px-4 py-2 text-sm font-medium text-[var(--esc-panel-text-secondary)] transition-colors hover:bg-[var(--esc-panel-hover)]"
47
- >
48
- Cancel
49
- </a>
50
- </div>
51
- </form>
52
- </EscalatedLayout>
53
- </template>
1
+ <script setup>
2
+ import { computed } from 'vue';
3
+ import EscalatedLayout from '../../../components/EscalatedLayout.vue';
4
+ import SkillTagManager from '../../../components/SkillTagManager.vue';
5
+ import { Link, useForm } from '@inertiajs/vue3';
6
+
7
+ const props = defineProps({
8
+ skill: { type: Object, default: null },
9
+ availableAgents: { type: Array, default: () => [] },
10
+ availableTags: { type: Array, default: () => [] },
11
+ availableDepartments: { type: Array, default: () => [] },
12
+ });
13
+
14
+ const form = useForm({
15
+ name: props.skill?.name || '',
16
+ routing_tag_ids: props.skill?.routing_tag_ids || [],
17
+ routing_department_ids: props.skill?.routing_department_ids || [],
18
+ agents: props.skill?.agents?.length ? props.skill.agents.map((agent) => ({ ...agent })) : [],
19
+ });
20
+
21
+ const unassignedAgents = computed(() =>
22
+ props.availableAgents.filter((agent) => !form.agents.some((entry) => entry.user_id === agent.id)),
23
+ );
24
+
25
+ function submit() {
26
+ if (props.skill) {
27
+ form.put(route('escalated.admin.skills.update', props.skill.id));
28
+ } else {
29
+ form.post(route('escalated.admin.skills.store'));
30
+ }
31
+ }
32
+
33
+ function addAgent() {
34
+ const nextAgent = unassignedAgents.value[0];
35
+ if (!nextAgent) {
36
+ return;
37
+ }
38
+
39
+ form.agents.push({ user_id: nextAgent.id, proficiency: 3 });
40
+ }
41
+
42
+ function removeAgent(index) {
43
+ form.agents.splice(index, 1);
44
+ }
45
+ </script>
46
+
47
+ <template>
48
+ <EscalatedLayout :title="skill ? 'Edit Skill' : 'Create Skill'">
49
+ <form
50
+ class="mx-auto max-w-4xl space-y-5 rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6"
51
+ @submit.prevent="submit"
52
+ >
53
+ <div>
54
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Name</label>
55
+ <input
56
+ v-model="form.name"
57
+ type="text"
58
+ required
59
+ class="mt-1 w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
60
+ />
61
+ <p v-if="form.errors.name" class="mt-1 text-xs text-rose-400">{{ form.errors.name }}</p>
62
+ </div>
63
+
64
+ <div class="grid gap-5 lg:grid-cols-2">
65
+ <div class="space-y-3 rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface-alt)] p-4">
66
+ <div>
67
+ <h2 class="text-sm font-semibold text-[var(--esc-panel-text-secondary)]">Routing Rules</h2>
68
+ <p class="mt-1 text-xs text-[var(--esc-panel-text-muted)]">
69
+ Match tickets by tag or department to require this skill during auto-assignment.
70
+ </p>
71
+ </div>
72
+ <div>
73
+ <label class="mb-2 block text-xs font-medium uppercase tracking-wide text-[var(--esc-panel-text-muted)]">
74
+ Required Tags
75
+ </label>
76
+ <SkillTagManager
77
+ v-model="form.routing_tag_ids"
78
+ :items="availableTags"
79
+ add-label="Add Tag"
80
+ empty-label="No ticket tags mapped"
81
+ />
82
+ <p v-if="form.errors.routing_tag_ids" class="mt-1 text-xs text-rose-400">
83
+ {{ form.errors.routing_tag_ids }}
84
+ </p>
85
+ </div>
86
+ <div>
87
+ <label class="mb-2 block text-xs font-medium uppercase tracking-wide text-[var(--esc-panel-text-muted)]">
88
+ Departments
89
+ </label>
90
+ <SkillTagManager
91
+ v-model="form.routing_department_ids"
92
+ :items="availableDepartments"
93
+ add-label="Add Department"
94
+ empty-label="No departments mapped"
95
+ />
96
+ <p v-if="form.errors.routing_department_ids" class="mt-1 text-xs text-rose-400">
97
+ {{ form.errors.routing_department_ids }}
98
+ </p>
99
+ </div>
100
+ </div>
101
+
102
+ <div class="space-y-3 rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface-alt)] p-4">
103
+ <div class="flex items-start justify-between gap-3">
104
+ <div>
105
+ <h2 class="text-sm font-semibold text-[var(--esc-panel-text-secondary)]">Agent Proficiency</h2>
106
+ <p class="mt-1 text-xs text-[var(--esc-panel-text-muted)]">
107
+ Assign eligible agents and tune their proficiency from 1 to 5.
108
+ </p>
109
+ </div>
110
+ <button
111
+ v-if="unassignedAgents.length"
112
+ type="button"
113
+ class="rounded-lg border border-dashed border-[var(--esc-panel-border-input)] px-3 py-1.5 text-xs font-medium text-[var(--esc-panel-text-secondary)] transition-colors hover:bg-[var(--esc-panel-hover)]"
114
+ @click="addAgent"
115
+ >
116
+ Add Agent
117
+ </button>
118
+ </div>
119
+
120
+ <div v-if="form.agents.length" class="space-y-3">
121
+ <div
122
+ v-for="(agent, index) in form.agents"
123
+ :key="`${agent.user_id}-${index}`"
124
+ class="grid gap-3 rounded-lg border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-3 md:grid-cols-[minmax(0,1fr)_110px_auto]"
125
+ >
126
+ <div>
127
+ <label class="mb-1 block text-xs font-medium uppercase tracking-wide text-[var(--esc-panel-text-muted)]">
128
+ Agent
129
+ </label>
130
+ <select
131
+ v-model="agent.user_id"
132
+ class="w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
133
+ >
134
+ <option
135
+ v-for="option in [...unassignedAgents, availableAgents.find((entry) => entry.id === agent.user_id)].filter(Boolean)"
136
+ :key="option.id"
137
+ :value="option.id"
138
+ >
139
+ {{ option.name }} · {{ option.email }}
140
+ </option>
141
+ </select>
142
+ </div>
143
+ <div>
144
+ <label class="mb-1 block text-xs font-medium uppercase tracking-wide text-[var(--esc-panel-text-muted)]">
145
+ Proficiency
146
+ </label>
147
+ <select
148
+ v-model="agent.proficiency"
149
+ class="w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-3 py-2 text-sm text-[var(--esc-panel-text-secondary)] focus:border-[var(--esc-panel-border-input)] focus:outline-none focus:ring-1 focus:ring-[var(--esc-panel-border-input)]"
150
+ >
151
+ <option :value="1">1</option>
152
+ <option :value="2">2</option>
153
+ <option :value="3">3</option>
154
+ <option :value="4">4</option>
155
+ <option :value="5">5</option>
156
+ </select>
157
+ </div>
158
+ <div class="flex items-end">
159
+ <button
160
+ type="button"
161
+ class="rounded-lg px-3 py-2 text-sm font-medium text-rose-300 transition-colors hover:bg-rose-500/10"
162
+ @click="removeAgent(index)"
163
+ >
164
+ Remove
165
+ </button>
166
+ </div>
167
+ </div>
168
+ </div>
169
+
170
+ <div v-else class="rounded-lg border border-dashed border-[var(--esc-panel-border)] px-4 py-6 text-center text-sm text-[var(--esc-panel-text-muted)]">
171
+ No agents assigned yet.
172
+ </div>
173
+ </div>
174
+ </div>
175
+
176
+ <div class="flex gap-3">
177
+ <button
178
+ type="submit"
179
+ :disabled="form.processing"
180
+ class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-2 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)] disabled:opacity-50"
181
+ >
182
+ {{ skill ? 'Update' : 'Create' }}
183
+ </button>
184
+ <Link
185
+ :href="route('escalated.admin.skills.index')"
186
+ class="rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-hover)] px-4 py-2 text-sm font-medium text-[var(--esc-panel-text-secondary)] transition-colors hover:bg-[var(--esc-panel-hover)]"
187
+ >
188
+ Cancel
189
+ </Link>
190
+ </div>
191
+ </form>
192
+ </EscalatedLayout>
193
+ </template>
@@ -1,94 +1,105 @@
1
- <script setup>
2
- import EscalatedLayout from '../../../components/EscalatedLayout.vue';
3
- import { Link, router } from '@inertiajs/vue3';
4
-
5
- defineProps({ skills: Array });
6
-
7
- function destroy(id) {
8
- if (confirm('Delete this skill?')) {
9
- router.delete(route('escalated.admin.skills.destroy', id));
10
- }
11
- }
12
- </script>
13
-
14
- <template>
15
- <EscalatedLayout title="Skills">
16
- <div class="mb-4 flex justify-end">
17
- <Link
18
- :href="route('escalated.admin.skills.create')"
19
- class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-2 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)]"
20
- >
21
- Add Skill
22
- </Link>
23
- </div>
24
- <div class="overflow-hidden rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
25
- <table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
26
- <thead>
27
- <tr class="bg-[var(--esc-panel-hover)]">
28
- <th
29
- class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
30
- >
31
- Name
32
- </th>
33
- <th
34
- class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
35
- >
36
- Agents
37
- </th>
38
- <th
39
- class="px-4 py-3 text-right text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
40
- >
41
- Actions
42
- </th>
43
- </tr>
44
- </thead>
45
- <tbody class="divide-y divide-[var(--esc-panel-border)]">
46
- <tr v-if="!skills?.length">
47
- <td colspan="3" class="px-4 py-12 text-center">
48
- <svg
49
- class="mx-auto mb-3 h-8 w-8 text-[var(--esc-panel-text-muted)]"
50
- fill="none"
51
- stroke="currentColor"
52
- stroke-width="1.5"
53
- viewBox="0 0 24 24"
54
- >
55
- <path
56
- stroke-linecap="round"
57
- stroke-linejoin="round"
58
- d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z"
59
- />
60
- </svg>
61
- <p class="text-sm text-[var(--esc-panel-text-muted)]">No skills yet</p>
62
- <p class="mt-1 text-xs text-[var(--esc-panel-text-muted)]">
63
- Create skills for agent routing
64
- </p>
65
- </td>
66
- </tr>
67
- <tr
68
- v-for="skill in skills"
69
- :key="skill.id"
70
- class="transition-colors hover:bg-[var(--esc-panel-hover)]"
71
- >
72
- <td class="px-4 py-3 text-sm font-medium text-[var(--esc-panel-text-secondary)]">
73
- {{ skill.name }}
74
- </td>
75
- <td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
76
- {{ skill.agents_count }}
77
- </td>
78
- <td class="px-4 py-3 text-right text-sm">
79
- <Link
80
- :href="route('escalated.admin.skills.edit', skill.id)"
81
- class="text-[var(--esc-panel-text-secondary)] hover:text-[var(--esc-panel-text)]"
82
- >
83
- Edit
84
- </Link>
85
- <button class="ml-3 text-rose-400 hover:text-rose-300" @click="destroy(skill.id)">
86
- Delete
87
- </button>
88
- </td>
89
- </tr>
90
- </tbody>
91
- </table>
92
- </div>
93
- </EscalatedLayout>
94
- </template>
1
+ <script setup>
2
+ import EscalatedLayout from '../../../components/EscalatedLayout.vue';
3
+ import { Link, router } from '@inertiajs/vue3';
4
+
5
+ defineProps({ skills: Array });
6
+
7
+ function destroy(id) {
8
+ if (confirm('Delete this skill?')) {
9
+ router.delete(route('escalated.admin.skills.destroy', id));
10
+ }
11
+ }
12
+ </script>
13
+
14
+ <template>
15
+ <EscalatedLayout title="Skills">
16
+ <div class="mb-4 flex justify-end">
17
+ <Link
18
+ :href="route('escalated.admin.skills.create')"
19
+ class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-4 py-2 text-sm font-medium text-white shadow-lg shadow-[var(--esc-panel-bg)]/20 transition-all hover:from-[var(--esc-panel-accent-hover)] hover:to-[var(--esc-panel-accent-secondary-hover)]"
20
+ >
21
+ Add Skill
22
+ </Link>
23
+ </div>
24
+ <div class="overflow-hidden rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)]">
25
+ <table class="min-w-full divide-y divide-[var(--esc-panel-border)]">
26
+ <thead>
27
+ <tr class="bg-[var(--esc-panel-hover)]">
28
+ <th
29
+ class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
30
+ >
31
+ Name
32
+ </th>
33
+ <th
34
+ class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
35
+ >
36
+ Routing
37
+ </th>
38
+ <th
39
+ class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
40
+ >
41
+ Updated
42
+ </th>
43
+ <th
44
+ class="px-4 py-3 text-right text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
45
+ >
46
+ Actions
47
+ </th>
48
+ </tr>
49
+ </thead>
50
+ <tbody class="divide-y divide-[var(--esc-panel-border)]">
51
+ <tr v-if="!skills?.length">
52
+ <td colspan="4" class="px-4 py-12 text-center">
53
+ <svg
54
+ class="mx-auto mb-3 h-8 w-8 text-[var(--esc-panel-text-muted)]"
55
+ fill="none"
56
+ stroke="currentColor"
57
+ stroke-width="1.5"
58
+ viewBox="0 0 24 24"
59
+ >
60
+ <path
61
+ stroke-linecap="round"
62
+ stroke-linejoin="round"
63
+ d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z"
64
+ />
65
+ </svg>
66
+ <p class="text-sm text-[var(--esc-panel-text-muted)]">No skills yet</p>
67
+ <p class="mt-1 text-xs text-[var(--esc-panel-text-muted)]">
68
+ Create skills for agent routing
69
+ </p>
70
+ </td>
71
+ </tr>
72
+ <tr
73
+ v-for="skill in skills"
74
+ :key="skill.id"
75
+ class="transition-colors hover:bg-[var(--esc-panel-hover)]"
76
+ >
77
+ <td class="px-4 py-3 text-sm font-medium text-[var(--esc-panel-text-secondary)]">
78
+ {{ skill.name }}
79
+ </td>
80
+ <td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
81
+ <div>{{ skill.agents_count }} agents</div>
82
+ <div class="text-xs text-[var(--esc-panel-text-muted)]">
83
+ {{ skill.routing_tags_count }} tags / {{ skill.routing_departments_count }} departments
84
+ </div>
85
+ </td>
86
+ <td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
87
+ {{ skill.updated_at ? new Date(skill.updated_at).toLocaleDateString() : '-' }}
88
+ </td>
89
+ <td class="px-4 py-3 text-right text-sm">
90
+ <Link
91
+ :href="route('escalated.admin.skills.edit', skill.id)"
92
+ class="text-[var(--esc-panel-text-secondary)] hover:text-[var(--esc-panel-text)]"
93
+ >
94
+ Edit
95
+ </Link>
96
+ <button class="ml-3 text-rose-400 hover:text-rose-300" @click="destroy(skill.id)">
97
+ Delete
98
+ </button>
99
+ </td>
100
+ </tr>
101
+ </tbody>
102
+ </table>
103
+ </div>
104
+ </EscalatedLayout>
105
+ </template>