@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,159 @@
1
+ <script setup>
2
+ import { computed } from 'vue';
3
+
4
+ const props = defineProps({
5
+ fields: { type: Array, required: true },
6
+ modelValue: { type: Object, default: () => ({}) },
7
+ errors: { type: Object, default: () => ({}) },
8
+ conditions: { type: Array, default: () => [] },
9
+ });
10
+
11
+ const emit = defineEmits(['update:modelValue']);
12
+
13
+ function updateField(name, value) {
14
+ emit('update:modelValue', { ...props.modelValue, [name]: value });
15
+ }
16
+
17
+ function toggleMultiSelect(name, option) {
18
+ const current = props.modelValue[name] || [];
19
+ const idx = current.indexOf(option);
20
+ const updated = idx >= 0 ? current.filter((_, i) => i !== idx) : [...current, option];
21
+ updateField(name, updated);
22
+ }
23
+
24
+ const sortedFields = computed(() => [...props.fields].sort((a, b) => (a.position || 0) - (b.position || 0)));
25
+
26
+ /**
27
+ * Evaluate whether a field should be visible based on conditions.
28
+ * Conditions can come from the `conditions` prop or from the field's own `conditions` property.
29
+ */
30
+ function isFieldVisible(field) {
31
+ // Merge global conditions and field-level conditions
32
+ const allConditions = [
33
+ ...props.conditions.filter((c) => c.target_field === field.name || c.target_field === field.slug),
34
+ ...(field.conditions || []).filter((c) => c.target_field === field.name || c.target_field === field.slug),
35
+ ];
36
+
37
+ // If no conditions target this field, it's always visible
38
+ if (allConditions.length === 0) {
39
+ return true;
40
+ }
41
+
42
+ // All conditions must be met (AND logic)
43
+ return allConditions.every((condition) => {
44
+ const currentValue = props.modelValue[condition.field];
45
+
46
+ switch (condition.operator) {
47
+ case 'equals':
48
+ return String(currentValue || '') === String(condition.value || '');
49
+ case 'not_equals':
50
+ return String(currentValue || '') !== String(condition.value || '');
51
+ case 'contains':
52
+ return String(currentValue || '').includes(String(condition.value || ''));
53
+ case 'is_empty':
54
+ return (
55
+ !currentValue || currentValue === '' || (Array.isArray(currentValue) && currentValue.length === 0)
56
+ );
57
+ case 'is_not_empty':
58
+ return (
59
+ currentValue && currentValue !== '' && !(Array.isArray(currentValue) && currentValue.length === 0)
60
+ );
61
+ default:
62
+ return true;
63
+ }
64
+ });
65
+ }
66
+ </script>
67
+
68
+ <template>
69
+ <div class="space-y-4">
70
+ <div v-for="field in sortedFields" v-show="isFieldVisible(field)" :key="field.id">
71
+ <label class="block text-sm font-medium text-neutral-300">
72
+ {{ field.label || field.name }}
73
+ <span v-if="field.required" class="text-rose-400">*</span>
74
+ </label>
75
+ <p v-if="field.description" class="mt-0.5 text-xs text-neutral-500">{{ field.description }}</p>
76
+
77
+ <!-- Text -->
78
+ <input
79
+ v-if="field.type === 'text'"
80
+ type="text"
81
+ :value="modelValue[field.name] || ''"
82
+ :placeholder="field.placeholder"
83
+ :required="field.required"
84
+ class="mt-1 w-full rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 placeholder-neutral-600 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10"
85
+ @input="updateField(field.name, $event.target.value)"
86
+ />
87
+
88
+ <!-- Textarea -->
89
+ <textarea
90
+ v-else-if="field.type === 'textarea'"
91
+ :value="modelValue[field.name] || ''"
92
+ :placeholder="field.placeholder"
93
+ :required="field.required"
94
+ rows="3"
95
+ class="mt-1 w-full rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 placeholder-neutral-600 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10"
96
+ @input="updateField(field.name, $event.target.value)"
97
+ ></textarea>
98
+
99
+ <!-- Number -->
100
+ <input
101
+ v-else-if="field.type === 'number'"
102
+ type="number"
103
+ :value="modelValue[field.name] || ''"
104
+ :placeholder="field.placeholder"
105
+ :required="field.required"
106
+ class="mt-1 w-full rounded-lg border border-white/10 bg-neutral-950 px-3 py-2 text-sm text-neutral-200 placeholder-neutral-600 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10"
107
+ @input="updateField(field.name, $event.target.value)"
108
+ />
109
+
110
+ <!-- Date -->
111
+ <input
112
+ v-else-if="field.type === 'date'"
113
+ type="date"
114
+ :value="modelValue[field.name] || ''"
115
+ :required="field.required"
116
+ 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"
117
+ @input="updateField(field.name, $event.target.value)"
118
+ />
119
+
120
+ <!-- Checkbox -->
121
+ <label v-else-if="field.type === 'checkbox'" class="mt-1 flex items-center gap-2">
122
+ <input
123
+ type="checkbox"
124
+ :checked="!!modelValue[field.name]"
125
+ class="rounded border-white/20 bg-neutral-900 text-cyan-500 focus:ring-white/10"
126
+ @change="updateField(field.name, $event.target.checked)"
127
+ />
128
+ <span class="text-sm text-neutral-300">{{ field.placeholder || 'Yes' }}</span>
129
+ </label>
130
+
131
+ <!-- Select -->
132
+ <select
133
+ v-else-if="field.type === 'select'"
134
+ :value="modelValue[field.name] || ''"
135
+ :required="field.required"
136
+ 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"
137
+ @change="updateField(field.name, $event.target.value)"
138
+ >
139
+ <option value="" disabled class="text-neutral-500">{{ field.placeholder || 'Select...' }}</option>
140
+ <option v-for="opt in field.options" :key="opt" :value="opt">{{ opt }}</option>
141
+ </select>
142
+
143
+ <!-- Multi-select (checkboxes) -->
144
+ <div v-else-if="field.type === 'multi_select'" class="mt-1 space-y-1">
145
+ <label v-for="opt in field.options" :key="opt" class="flex items-center gap-2">
146
+ <input
147
+ type="checkbox"
148
+ :checked="(modelValue[field.name] || []).includes(opt)"
149
+ class="rounded border-white/20 bg-neutral-900 text-cyan-500 focus:ring-white/10"
150
+ @change="toggleMultiSelect(field.name, opt)"
151
+ />
152
+ <span class="text-sm text-neutral-300">{{ opt }}</span>
153
+ </label>
154
+ </div>
155
+
156
+ <div v-if="errors[field.name]" class="mt-1 text-sm text-rose-400">{{ errors[field.name] }}</div>
157
+ </div>
158
+ </div>
159
+ </template>