@escalated-dev/escalated 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +125 -2
  3. package/package.json +40 -5
  4. package/src/components/ActiveChatsPanel.vue +246 -0
  5. package/src/components/ActivityTimeline.stories.js +55 -0
  6. package/src/components/ActivityTimeline.vue +83 -66
  7. package/src/components/AdminDashboard.stories.js +1423 -0
  8. package/src/components/AgentLoadIndicator.stories.js +37 -0
  9. package/src/components/AgentLoadIndicator.vue +49 -0
  10. package/src/components/AgentPerformanceCard.vue +44 -0
  11. package/src/components/ArticleEditor.vue +82 -0
  12. package/src/components/AssigneeSelect.vue +37 -23
  13. package/src/components/AttachmentList.vue +65 -37
  14. package/src/components/AuditLogEntry.vue +95 -0
  15. package/src/components/BulkActionBar.vue +166 -97
  16. package/src/components/CategoryTree.vue +89 -0
  17. package/src/components/ChartWidget.vue +46 -0
  18. package/src/components/ChatActionBar.vue +226 -0
  19. package/src/components/ChatBubble.vue +162 -0
  20. package/src/components/ChatComposer.vue +308 -0
  21. package/src/components/ChatQueue.vue +222 -0
  22. package/src/components/ChatThread.vue +197 -0
  23. package/src/components/CollisionWarning.stories.js +30 -0
  24. package/src/components/CollisionWarning.vue +68 -0
  25. package/src/components/ConditionalFieldRules.vue +135 -0
  26. package/src/components/ContextPanel.vue +66 -0
  27. package/src/components/ContextPanelSection.vue +65 -0
  28. package/src/components/CustomFieldRenderer.vue +159 -0
  29. package/src/components/EscalatedLayout.vue +424 -70
  30. package/src/components/FileDropzone.vue +57 -41
  31. package/src/components/FollowButton.stories.js +30 -0
  32. package/src/components/FollowButton.vue +88 -63
  33. package/src/components/KeyboardShortcutHelp.vue +213 -123
  34. package/src/components/KnowledgePanel.vue +122 -0
  35. package/src/components/KpiCard.stories.js +32 -0
  36. package/src/components/KpiCard.vue +88 -0
  37. package/src/components/MacroDropdown.vue +150 -90
  38. package/src/components/MentionDropdown.vue +154 -0
  39. package/src/components/MetricComparison.vue +89 -0
  40. package/src/components/PercentileChart.vue +67 -0
  41. package/src/components/PeriodSelector.vue +98 -0
  42. package/src/components/PermissionMatrix.vue +107 -0
  43. package/src/components/PinnedNotes.vue +129 -90
  44. package/src/components/PluginSlot.vue +68 -62
  45. package/src/components/PresenceIndicator.vue +138 -28
  46. package/src/components/PriorityBadge.stories.js +33 -0
  47. package/src/components/PriorityBadge.vue +46 -39
  48. package/src/components/QuickFilters.vue +65 -49
  49. package/src/components/ReplyComposer.vue +207 -30
  50. package/src/components/ReplyThread.vue +111 -21
  51. package/src/components/ReportChart.vue +395 -0
  52. package/src/components/ReportExportButton.vue +108 -0
  53. package/src/components/SatisfactionRating.vue +180 -128
  54. package/src/components/SaveViewDialog.vue +172 -0
  55. package/src/components/SavedViewSidebar.vue +208 -0
  56. package/src/components/ScheduleEditor.vue +104 -0
  57. package/src/components/SideConversation.vue +165 -0
  58. package/src/components/SideConversationList.vue +147 -0
  59. package/src/components/SkillTagManager.vue +79 -0
  60. package/src/components/SlaComplianceChart.vue +46 -0
  61. package/src/components/SlaTimer.stories.js +35 -0
  62. package/src/components/SlaTimer.vue +62 -49
  63. package/src/components/SnoozeButton.vue +247 -0
  64. package/src/components/StatsCard.stories.js +24 -0
  65. package/src/components/StatsCard.vue +18 -6
  66. package/src/components/StatusBadge.stories.js +53 -0
  67. package/src/components/StatusBadge.vue +78 -46
  68. package/src/components/TagSelect.vue +84 -53
  69. package/src/components/TicketFilters.vue +247 -46
  70. package/src/components/TicketLinkPanel.vue +193 -0
  71. package/src/components/TicketList.vue +536 -74
  72. package/src/components/TicketMergeDialog.vue +157 -0
  73. package/src/components/TicketSidebar.vue +167 -99
  74. package/src/components/TicketSplitDialog.vue +98 -0
  75. package/src/components/TicketTypeSelector.vue +21 -0
  76. package/src/components/TotpInput.stories.js +9 -0
  77. package/src/components/TotpInput.vue +81 -0
  78. package/src/components/TwoFactorSetup.vue +113 -0
  79. package/src/components/VariableTokenInput.vue +111 -0
  80. package/src/components/WorkflowActionConfig.vue +287 -0
  81. package/src/components/WorkflowActionList.vue +350 -0
  82. package/src/components/WorkflowConditionBuilder.vue +265 -0
  83. package/src/components/WorkflowTriggerSelector.vue +125 -0
  84. package/src/composables/useChat.js +187 -0
  85. package/src/composables/useDebouncedSearch.js +38 -0
  86. package/src/composables/useI18n.js +80 -80
  87. package/src/composables/useKeyboardShortcuts.js +46 -46
  88. package/src/composables/useMentions.js +134 -0
  89. package/src/composables/usePluginExtensions.js +159 -67
  90. package/src/composables/usePluginHooks.js +65 -0
  91. package/src/composables/useRealtime.js +182 -0
  92. package/src/defineEscalatedPlugin.js +76 -50
  93. package/src/index.js +77 -28
  94. package/src/locales/ar.json +727 -0
  95. package/src/locales/de.json +678 -430
  96. package/src/locales/en.json +717 -430
  97. package/src/locales/es.json +679 -430
  98. package/src/locales/fr.json +680 -431
  99. package/src/locales/index.js +12 -2
  100. package/src/locales/it.json +727 -0
  101. package/src/locales/ja.json +727 -0
  102. package/src/locales/ko.json +727 -0
  103. package/src/locales/nl.json +727 -0
  104. package/src/locales/pl.json +727 -0
  105. package/src/locales/pt-BR.json +727 -0
  106. package/src/locales/ru.json +727 -0
  107. package/src/locales/tr.json +727 -0
  108. package/src/locales/zh-CN.json +727 -0
  109. package/src/pages/Admin/ApiTokens/Index.vue +381 -217
  110. package/src/pages/Admin/AuditLog/Index.vue +154 -0
  111. package/src/pages/Admin/BusinessHours/Form.vue +172 -0
  112. package/src/pages/Admin/BusinessHours/Index.vue +113 -0
  113. package/src/pages/Admin/CannedResponses/Index.vue +82 -25
  114. package/src/pages/Admin/Capacity/Index.vue +134 -0
  115. package/src/pages/Admin/CustomFields/Form.vue +195 -0
  116. package/src/pages/Admin/CustomFields/Index.vue +134 -0
  117. package/src/pages/Admin/CustomObjects/Form.vue +202 -0
  118. package/src/pages/Admin/CustomObjects/Index.vue +127 -0
  119. package/src/pages/Admin/CustomObjects/Records.vue +228 -0
  120. package/src/pages/Admin/Departments/Form.vue +35 -11
  121. package/src/pages/Admin/Departments/Index.vue +74 -21
  122. package/src/pages/Admin/EscalationRules/Form.vue +86 -25
  123. package/src/pages/Admin/EscalationRules/Index.vue +158 -37
  124. package/src/pages/Admin/Import/Connect.vue +95 -0
  125. package/src/pages/Admin/Import/Index.vue +102 -0
  126. package/src/pages/Admin/Import/Mapping.vue +110 -0
  127. package/src/pages/Admin/Import/Progress.vue +153 -0
  128. package/src/pages/Admin/Import/Review.vue +76 -0
  129. package/src/pages/Admin/KnowledgeBase/Articles/Form.vue +142 -0
  130. package/src/pages/Admin/KnowledgeBase/Articles/Index.vue +195 -0
  131. package/src/pages/Admin/KnowledgeBase/Categories/Index.vue +193 -0
  132. package/src/pages/Admin/Macros/Index.vue +448 -287
  133. package/src/pages/Admin/Plugins/Index.vue +327 -197
  134. package/src/pages/Admin/Reports/AgentDetail.vue +132 -0
  135. package/src/pages/Admin/Reports/AgentMetrics.vue +115 -0
  136. package/src/pages/Admin/Reports/AgentRanking.vue +246 -0
  137. package/src/pages/Admin/Reports/Cohorts.vue +180 -0
  138. package/src/pages/Admin/Reports/Comparison.vue +141 -0
  139. package/src/pages/Admin/Reports/CsatReport.vue +127 -0
  140. package/src/pages/Admin/Reports/Dashboard.vue +226 -0
  141. package/src/pages/Admin/Reports/ResolutionTimes.vue +164 -0
  142. package/src/pages/Admin/Reports/ResponseTimes.vue +164 -0
  143. package/src/pages/Admin/Reports/SlaReport.vue +162 -0
  144. package/src/pages/Admin/Reports/SlaTrends.vue +183 -0
  145. package/src/pages/Admin/Reports.vue +206 -101
  146. package/src/pages/Admin/Roles/Form.vue +71 -0
  147. package/src/pages/Admin/Roles/Index.vue +122 -0
  148. package/src/pages/Admin/Settings/CsatSettings.vue +165 -0
  149. package/src/pages/Admin/Settings/DataRetention.vue +182 -0
  150. package/src/pages/Admin/Settings/EmailSettings.vue +243 -0
  151. package/src/pages/Admin/Settings/Sandbox.vue +135 -0
  152. package/src/pages/Admin/Settings/SsoSettings.vue +218 -0
  153. package/src/pages/Admin/Settings/TwoFactor.vue +110 -0
  154. package/src/pages/Admin/Settings.vue +847 -99
  155. package/src/pages/Admin/Skills/Form.vue +53 -0
  156. package/src/pages/Admin/Skills/Index.vue +94 -0
  157. package/src/pages/Admin/SlaPolicies/Form.vue +62 -19
  158. package/src/pages/Admin/SlaPolicies/Index.vue +85 -23
  159. package/src/pages/Admin/Statuses/Form.vue +114 -0
  160. package/src/pages/Admin/Statuses/Index.vue +153 -0
  161. package/src/pages/Admin/Tags/Index.vue +121 -35
  162. package/src/pages/Admin/Tickets/Index.vue +96 -9
  163. package/src/pages/Admin/Tickets/Show.vue +130 -37
  164. package/src/pages/Admin/Webhooks/DeliveryLog.vue +120 -0
  165. package/src/pages/Admin/Webhooks/Form.vue +110 -0
  166. package/src/pages/Admin/Webhooks/Index.vue +136 -0
  167. package/src/pages/Admin/Workflows/Builder.vue +207 -0
  168. package/src/pages/Admin/Workflows/Index.vue +235 -0
  169. package/src/pages/Admin/Workflows/Logs.vue +286 -0
  170. package/src/pages/Agent/Dashboard.vue +311 -186
  171. package/src/pages/Agent/TicketIndex.vue +80 -6
  172. package/src/pages/Agent/TicketShow.vue +399 -60
  173. package/src/pages/Customer/Create.vue +37 -10
  174. package/src/pages/Customer/Index.vue +9 -2
  175. package/src/pages/Customer/KnowledgeBase/Article.vue +129 -0
  176. package/src/pages/Customer/KnowledgeBase/Index.vue +151 -0
  177. package/src/pages/Customer/Show.vue +69 -63
  178. package/src/pages/Guest/Create.vue +40 -15
  179. package/src/pages/Guest/Show.vue +124 -93
  180. package/src/plugin.js +111 -1
  181. package/src/utils/constants.js +30 -0
  182. package/src/utils/formatting.js +35 -0
  183. package/src/utils/mentionHighlight.js +24 -0
  184. package/src/utils/sanitizeHtml.js +78 -64
  185. package/src/widget/EscalatedWidget.vue +804 -0
  186. package/src/widget/index.js +254 -0
  187. package/src/pages/Admin/Plugins.vue +0 -129
@@ -0,0 +1,53 @@
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>
@@ -0,0 +1,94 @@
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>
@@ -25,48 +25,91 @@ function submit() {
25
25
 
26
26
  <template>
27
27
  <EscalatedLayout :title="policy ? 'Edit SLA Policy' : 'New SLA Policy'">
28
- <form @submit.prevent="submit" class="mx-auto max-w-lg space-y-5 rounded-xl border border-white/[0.06] bg-neutral-900/60 p-6">
28
+ <form
29
+ class="mx-auto max-w-lg space-y-5 rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6"
30
+ @submit.prevent="submit"
31
+ >
29
32
  <div>
30
- <label class="block text-sm font-medium text-neutral-300">Name</label>
31
- <input v-model="form.name" type="text" required class="mt-1 w-full rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
33
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Name</label>
34
+ <input
35
+ v-model="form.name"
36
+ type="text"
37
+ required
38
+ 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)]"
39
+ />
32
40
  </div>
33
41
  <div>
34
- <label class="block text-sm font-medium text-neutral-300">Description</label>
35
- <textarea v-model="form.description" rows="2" class="mt-1 w-full rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10"></textarea>
42
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Description</label>
43
+ <textarea
44
+ v-model="form.description"
45
+ rows="2"
46
+ 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)]"
47
+ ></textarea>
36
48
  </div>
37
49
  <div>
38
- <h3 class="mb-2 text-sm font-medium text-neutral-300">First Response Hours (by priority)</h3>
50
+ <h3 class="mb-2 text-sm font-medium text-[var(--esc-panel-text-secondary)]">
51
+ First Response Hours (by priority)
52
+ </h3>
39
53
  <div class="grid grid-cols-2 gap-3">
40
54
  <div v-for="p in priorities" :key="p">
41
- <label class="text-xs capitalize text-neutral-400">{{ p }}</label>
42
- <input v-model.number="form.first_response_hours[p]" type="number" step="0.5" min="0" class="w-full rounded-lg border border-white/10 bg-neutral-950 px-2 py-1.5 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
55
+ <label class="text-xs capitalize text-[var(--esc-panel-text-tertiary)]">{{ p }}</label>
56
+ <input
57
+ v-model.number="form.first_response_hours[p]"
58
+ type="number"
59
+ step="0.5"
60
+ min="0"
61
+ class="w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-2 py-1.5 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)]"
62
+ />
43
63
  </div>
44
64
  </div>
45
65
  </div>
46
66
  <div>
47
- <h3 class="mb-2 text-sm font-medium text-neutral-300">Resolution Hours (by priority)</h3>
67
+ <h3 class="mb-2 text-sm font-medium text-[var(--esc-panel-text-secondary)]">
68
+ Resolution Hours (by priority)
69
+ </h3>
48
70
  <div class="grid grid-cols-2 gap-3">
49
71
  <div v-for="p in priorities" :key="p">
50
- <label class="text-xs capitalize text-neutral-400">{{ p }}</label>
51
- <input v-model.number="form.resolution_hours[p]" type="number" step="0.5" min="0" class="w-full rounded-lg border border-white/10 bg-neutral-950 px-2 py-1.5 text-sm text-neutral-200 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" />
72
+ <label class="text-xs capitalize text-[var(--esc-panel-text-tertiary)]">{{ p }}</label>
73
+ <input
74
+ v-model.number="form.resolution_hours[p]"
75
+ type="number"
76
+ step="0.5"
77
+ min="0"
78
+ class="w-full rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)] px-2 py-1.5 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)]"
79
+ />
52
80
  </div>
53
81
  </div>
54
82
  </div>
55
83
  <label class="flex items-center gap-2">
56
- <input v-model="form.business_hours_only" type="checkbox" class="rounded border-white/20 bg-neutral-900 text-white focus:ring-white/10" />
57
- <span class="text-sm text-neutral-300">Business hours only</span>
84
+ <input
85
+ v-model="form.business_hours_only"
86
+ type="checkbox"
87
+ class="rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] text-white focus:ring-[var(--esc-panel-border-input)]"
88
+ />
89
+ <span class="text-sm text-[var(--esc-panel-text-secondary)]">Business hours only</span>
58
90
  </label>
59
91
  <label class="flex items-center gap-2">
60
- <input v-model="form.is_default" type="checkbox" class="rounded border-white/20 bg-neutral-900 text-white focus:ring-white/10" />
61
- <span class="text-sm text-neutral-300">Default policy</span>
92
+ <input
93
+ v-model="form.is_default"
94
+ type="checkbox"
95
+ class="rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] text-white focus:ring-[var(--esc-panel-border-input)]"
96
+ />
97
+ <span class="text-sm text-[var(--esc-panel-text-secondary)]">Default policy</span>
62
98
  </label>
63
99
  <label class="flex items-center gap-2">
64
- <input v-model="form.is_active" type="checkbox" class="rounded border-white/20 bg-neutral-900 text-white focus:ring-white/10" />
65
- <span class="text-sm text-neutral-300">Active</span>
100
+ <input
101
+ v-model="form.is_active"
102
+ type="checkbox"
103
+ class="rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] text-white focus:ring-[var(--esc-panel-border-input)]"
104
+ />
105
+ <span class="text-sm text-[var(--esc-panel-text-secondary)]">Active</span>
66
106
  </label>
67
107
  <div class="flex justify-end">
68
- <button type="submit" :disabled="form.processing"
69
- class="rounded-lg bg-gradient-to-r from-cyan-500 to-violet-500 px-5 py-2 text-sm font-medium text-white shadow-lg shadow-black/20 transition-all hover:from-cyan-400 hover:to-violet-400 disabled:opacity-50">
108
+ <button
109
+ type="submit"
110
+ :disabled="form.processing"
111
+ class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-5 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"
112
+ >
70
113
  {{ policy ? 'Update' : 'Create' }}
71
114
  </button>
72
115
  </div>
@@ -14,42 +14,104 @@ function destroy(id) {
14
14
  <template>
15
15
  <EscalatedLayout title="SLA Policies">
16
16
  <div class="mb-4 flex justify-end">
17
- <Link :href="route('escalated.admin.sla-policies.create')"
18
- class="rounded-lg bg-gradient-to-r from-cyan-500 to-violet-500 px-4 py-2 text-sm font-medium text-white shadow-lg shadow-black/20 transition-all hover:from-cyan-400 hover:to-violet-400">
17
+ <Link
18
+ :href="route('escalated.admin.sla-policies.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
+ >
19
21
  Add Policy
20
22
  </Link>
21
23
  </div>
22
- <div class="overflow-hidden rounded-xl border border-white/[0.06] bg-neutral-900/60">
23
- <table class="min-w-full divide-y divide-white/[0.06]">
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)]">
24
26
  <thead>
25
- <tr class="bg-white/[0.02]">
26
- <th class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-neutral-500">Name</th>
27
- <th class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-neutral-500">Default</th>
28
- <th class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-neutral-500">Business Hours</th>
29
- <th class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-neutral-500">Tickets</th>
30
- <th class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-neutral-500">Active</th>
31
- <th class="px-4 py-3 text-right text-[11px] font-semibold uppercase tracking-wider text-neutral-500">Actions</th>
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
+ Default
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
+ Business Hours
42
+ </th>
43
+ <th
44
+ class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
45
+ >
46
+ Tickets
47
+ </th>
48
+ <th
49
+ class="px-4 py-3 text-left text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
50
+ >
51
+ Active
52
+ </th>
53
+ <th
54
+ class="px-4 py-3 text-right text-[11px] font-semibold uppercase tracking-wider text-[var(--esc-panel-text-muted)]"
55
+ >
56
+ Actions
57
+ </th>
32
58
  </tr>
33
59
  </thead>
34
- <tbody class="divide-y divide-white/[0.04]">
60
+ <tbody class="divide-y divide-[var(--esc-panel-border)]">
35
61
  <tr v-if="!policies?.length">
36
62
  <td colspan="6" class="px-4 py-12 text-center">
37
- <svg class="mx-auto mb-3 h-8 w-8 text-neutral-700" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
38
- <p class="text-sm text-neutral-500">No SLA policies yet</p>
39
- <p class="mt-1 text-xs text-neutral-600">Define response and resolution time targets</p>
63
+ <svg
64
+ class="mx-auto mb-3 h-8 w-8 text-[var(--esc-panel-text-muted)]"
65
+ fill="none"
66
+ stroke="currentColor"
67
+ stroke-width="1.5"
68
+ viewBox="0 0 24 24"
69
+ >
70
+ <path
71
+ stroke-linecap="round"
72
+ stroke-linejoin="round"
73
+ d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
74
+ />
75
+ </svg>
76
+ <p class="text-sm text-[var(--esc-panel-text-muted)]">No SLA policies yet</p>
77
+ <p class="mt-1 text-xs text-[var(--esc-panel-text-muted)]">
78
+ Define response and resolution time targets
79
+ </p>
40
80
  </td>
41
81
  </tr>
42
- <tr v-for="policy in policies" :key="policy.id" class="transition-colors hover:bg-white/[0.03]">
43
- <td class="px-4 py-3 text-sm font-medium text-neutral-200">{{ policy.name }}</td>
44
- <td class="px-4 py-3 text-sm text-neutral-400">{{ policy.is_default ? 'Yes' : 'No' }}</td>
45
- <td class="px-4 py-3 text-sm text-neutral-400">{{ policy.business_hours_only ? 'Yes' : 'No' }}</td>
46
- <td class="px-4 py-3 text-sm text-neutral-400">{{ policy.tickets_count }}</td>
82
+ <tr
83
+ v-for="policy in policies"
84
+ :key="policy.id"
85
+ class="transition-colors hover:bg-[var(--esc-panel-hover)]"
86
+ >
87
+ <td class="px-4 py-3 text-sm font-medium text-[var(--esc-panel-text-secondary)]">
88
+ {{ policy.name }}
89
+ </td>
90
+ <td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
91
+ {{ policy.is_default ? 'Yes' : 'No' }}
92
+ </td>
93
+ <td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
94
+ {{ policy.business_hours_only ? 'Yes' : 'No' }}
95
+ </td>
96
+ <td class="px-4 py-3 text-sm text-[var(--esc-panel-text-tertiary)]">
97
+ {{ policy.tickets_count }}
98
+ </td>
47
99
  <td class="px-4 py-3 text-sm">
48
- <span :class="policy.is_active ? 'text-emerald-400' : 'text-neutral-500'">{{ policy.is_active ? 'Yes' : 'No' }}</span>
100
+ <span
101
+ :class="policy.is_active ? 'text-emerald-400' : 'text-[var(--esc-panel-text-muted)]'"
102
+ >{{ policy.is_active ? 'Yes' : 'No' }}</span
103
+ >
49
104
  </td>
50
105
  <td class="px-4 py-3 text-right text-sm">
51
- <Link :href="route('escalated.admin.sla-policies.edit', policy.id)" class="text-neutral-300 hover:text-white">Edit</Link>
52
- <button @click="destroy(policy.id)" class="ml-3 text-rose-400 hover:text-rose-300">Delete</button>
106
+ <Link
107
+ :href="route('escalated.admin.sla-policies.edit', policy.id)"
108
+ class="text-[var(--esc-panel-text-secondary)] hover:text-[var(--esc-panel-text)]"
109
+ >
110
+ Edit
111
+ </Link>
112
+ <button class="ml-3 text-rose-400 hover:text-rose-300" @click="destroy(policy.id)">
113
+ Delete
114
+ </button>
53
115
  </td>
54
116
  </tr>
55
117
  </tbody>
@@ -0,0 +1,114 @@
1
+ <script setup>
2
+ import EscalatedLayout from '../../../components/EscalatedLayout.vue';
3
+ import { useForm } from '@inertiajs/vue3';
4
+
5
+ const props = defineProps({
6
+ status: { type: Object, default: null },
7
+ categories: { type: Array, default: () => ['new', 'open', 'pending', 'on_hold', 'solved'] },
8
+ });
9
+
10
+ const categoryLabels = {
11
+ new: 'New',
12
+ open: 'Open',
13
+ pending: 'Pending',
14
+ on_hold: 'On-Hold',
15
+ solved: 'Solved',
16
+ };
17
+
18
+ const form = useForm({
19
+ label: props.status?.label || '',
20
+ category: props.status?.category || 'open',
21
+ color: props.status?.color || '#6b7280',
22
+ description: props.status?.description || '',
23
+ position: props.status?.position ?? 0,
24
+ is_default: props.status?.is_default ?? false,
25
+ });
26
+
27
+ function submit() {
28
+ if (props.status) {
29
+ form.put(route('escalated.admin.statuses.update', props.status.id));
30
+ } else {
31
+ form.post(route('escalated.admin.statuses.store'));
32
+ }
33
+ }
34
+ </script>
35
+
36
+ <template>
37
+ <EscalatedLayout :title="status ? 'Edit Status' : 'New Status'">
38
+ <form
39
+ class="mx-auto max-w-lg space-y-5 rounded-xl border border-[var(--esc-panel-border)] bg-[var(--esc-panel-surface)] p-6"
40
+ @submit.prevent="submit"
41
+ >
42
+ <div>
43
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Label</label>
44
+ <input
45
+ v-model="form.label"
46
+ type="text"
47
+ required
48
+ 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)]"
49
+ />
50
+ <div v-if="form.errors.label" class="mt-1 text-sm text-rose-400">{{ form.errors.label }}</div>
51
+ </div>
52
+
53
+ <div>
54
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Category</label>
55
+ <select
56
+ v-model="form.category"
57
+ required
58
+ 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)]"
59
+ >
60
+ <option v-for="cat in categories" :key="cat" :value="cat">{{ categoryLabels[cat] || cat }}</option>
61
+ </select>
62
+ <div v-if="form.errors.category" class="mt-1 text-sm text-rose-400">{{ form.errors.category }}</div>
63
+ </div>
64
+
65
+ <div>
66
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Color</label>
67
+ <input
68
+ v-model="form.color"
69
+ type="color"
70
+ class="mt-1 h-10 w-16 rounded-lg border border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface-alt)]"
71
+ />
72
+ <div v-if="form.errors.color" class="mt-1 text-sm text-rose-400">{{ form.errors.color }}</div>
73
+ </div>
74
+
75
+ <div>
76
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Description</label>
77
+ <textarea
78
+ v-model="form.description"
79
+ rows="2"
80
+ 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)]"
81
+ ></textarea>
82
+ </div>
83
+
84
+ <div>
85
+ <label class="block text-sm font-medium text-[var(--esc-panel-text-secondary)]">Position</label>
86
+ <input
87
+ v-model.number="form.position"
88
+ type="number"
89
+ min="0"
90
+ 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)]"
91
+ />
92
+ </div>
93
+
94
+ <label class="flex items-center gap-2">
95
+ <input
96
+ v-model="form.is_default"
97
+ type="checkbox"
98
+ class="rounded border-[var(--esc-panel-border-input)] bg-[var(--esc-panel-surface)] text-cyan-500 focus:ring-[var(--esc-panel-border-input)]"
99
+ />
100
+ <span class="text-sm text-[var(--esc-panel-text-secondary)]">Default status for this category</span>
101
+ </label>
102
+
103
+ <div class="flex justify-end">
104
+ <button
105
+ type="submit"
106
+ :disabled="form.processing"
107
+ class="rounded-lg bg-gradient-to-r from-[var(--esc-panel-accent)] to-[var(--esc-panel-accent-secondary)] px-5 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"
108
+ >
109
+ {{ status ? 'Update' : 'Create' }}
110
+ </button>
111
+ </div>
112
+ </form>
113
+ </EscalatedLayout>
114
+ </template>