@primocaredentgroup/elettromedicali 0.1.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 (197) hide show
  1. package/dist/client/index.d.ts +72 -0
  2. package/dist/client/index.d.ts.map +1 -0
  3. package/dist/client/index.js +233 -0
  4. package/dist/client/index.js.map +1 -0
  5. package/dist/component/_generated/api.d.ts +94 -0
  6. package/dist/component/_generated/api.d.ts.map +1 -0
  7. package/dist/component/_generated/api.js +31 -0
  8. package/dist/component/_generated/api.js.map +1 -0
  9. package/dist/component/_generated/component.d.ts +1444 -0
  10. package/dist/component/_generated/component.d.ts.map +1 -0
  11. package/dist/component/_generated/component.js +11 -0
  12. package/dist/component/_generated/component.js.map +1 -0
  13. package/dist/component/_generated/dataModel.d.ts +46 -0
  14. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  15. package/dist/component/_generated/dataModel.js +11 -0
  16. package/dist/component/_generated/dataModel.js.map +1 -0
  17. package/dist/component/_generated/server.d.ts +121 -0
  18. package/dist/component/_generated/server.d.ts.map +1 -0
  19. package/dist/component/_generated/server.js +78 -0
  20. package/dist/component/_generated/server.js.map +1 -0
  21. package/dist/component/apiKeys.d.ts +69 -0
  22. package/dist/component/apiKeys.d.ts.map +1 -0
  23. package/dist/component/apiKeys.js +207 -0
  24. package/dist/component/apiKeys.js.map +1 -0
  25. package/dist/component/clinics.d.ts +103 -0
  26. package/dist/component/clinics.d.ts.map +1 -0
  27. package/dist/component/clinics.js +126 -0
  28. package/dist/component/clinics.js.map +1 -0
  29. package/dist/component/contracts.d.ts +85 -0
  30. package/dist/component/contracts.d.ts.map +1 -0
  31. package/dist/component/contracts.js +115 -0
  32. package/dist/component/contracts.js.map +1 -0
  33. package/dist/component/convex.config.d.ts +3 -0
  34. package/dist/component/convex.config.d.ts.map +1 -0
  35. package/dist/component/convex.config.js +3 -0
  36. package/dist/component/convex.config.js.map +1 -0
  37. package/dist/component/crons.d.ts +3 -0
  38. package/dist/component/crons.d.ts.map +1 -0
  39. package/dist/component/crons.js +7 -0
  40. package/dist/component/crons.js.map +1 -0
  41. package/dist/component/dashboardStats.d.ts +14 -0
  42. package/dist/component/dashboardStats.d.ts.map +1 -0
  43. package/dist/component/dashboardStats.js +136 -0
  44. package/dist/component/dashboardStats.js.map +1 -0
  45. package/dist/component/dashboardStatsCache.d.ts +32 -0
  46. package/dist/component/dashboardStatsCache.d.ts.map +1 -0
  47. package/dist/component/dashboardStatsCache.js +129 -0
  48. package/dist/component/dashboardStatsCache.js.map +1 -0
  49. package/dist/component/deviceCategories.d.ts +108 -0
  50. package/dist/component/deviceCategories.d.ts.map +1 -0
  51. package/dist/component/deviceCategories.js +254 -0
  52. package/dist/component/deviceCategories.js.map +1 -0
  53. package/dist/component/deviceQuestions.d.ts +129 -0
  54. package/dist/component/deviceQuestions.d.ts.map +1 -0
  55. package/dist/component/deviceQuestions.js +175 -0
  56. package/dist/component/deviceQuestions.js.map +1 -0
  57. package/dist/component/deviceRepairHistory.d.ts +30 -0
  58. package/dist/component/deviceRepairHistory.d.ts.map +1 -0
  59. package/dist/component/deviceRepairHistory.js +84 -0
  60. package/dist/component/deviceRepairHistory.js.map +1 -0
  61. package/dist/component/deviceStatus.d.ts +63 -0
  62. package/dist/component/deviceStatus.d.ts.map +1 -0
  63. package/dist/component/deviceStatus.js +58 -0
  64. package/dist/component/deviceStatus.js.map +1 -0
  65. package/dist/component/devices.d.ts +299 -0
  66. package/dist/component/devices.d.ts.map +1 -0
  67. package/dist/component/devices.js +587 -0
  68. package/dist/component/devices.js.map +1 -0
  69. package/dist/component/emailHelpers.d.ts +17 -0
  70. package/dist/component/emailHelpers.d.ts.map +1 -0
  71. package/dist/component/emailHelpers.js +39 -0
  72. package/dist/component/emailHelpers.js.map +1 -0
  73. package/dist/component/emails.d.ts +56 -0
  74. package/dist/component/emails.d.ts.map +1 -0
  75. package/dist/component/emails.js +58 -0
  76. package/dist/component/emails.js.map +1 -0
  77. package/dist/component/http.d.ts +3 -0
  78. package/dist/component/http.d.ts.map +1 -0
  79. package/dist/component/http.js +229 -0
  80. package/dist/component/http.js.map +1 -0
  81. package/dist/component/maintenanceTasks.d.ts +733 -0
  82. package/dist/component/maintenanceTasks.d.ts.map +1 -0
  83. package/dist/component/maintenanceTasks.js +937 -0
  84. package/dist/component/maintenanceTasks.js.map +1 -0
  85. package/dist/component/roles.d.ts +75 -0
  86. package/dist/component/roles.d.ts.map +1 -0
  87. package/dist/component/roles.js +98 -0
  88. package/dist/component/roles.js.map +1 -0
  89. package/dist/component/schema.d.ts +1295 -0
  90. package/dist/component/schema.d.ts.map +1 -0
  91. package/dist/component/schema.js +724 -0
  92. package/dist/component/schema.js.map +1 -0
  93. package/dist/component/slaMonitoring.d.ts +32 -0
  94. package/dist/component/slaMonitoring.d.ts.map +1 -0
  95. package/dist/component/slaMonitoring.js +111 -0
  96. package/dist/component/slaMonitoring.js.map +1 -0
  97. package/dist/component/slaRules.d.ts +72 -0
  98. package/dist/component/slaRules.d.ts.map +1 -0
  99. package/dist/component/slaRules.js +193 -0
  100. package/dist/component/slaRules.js.map +1 -0
  101. package/dist/component/sparePartOrders.d.ts +177 -0
  102. package/dist/component/sparePartOrders.d.ts.map +1 -0
  103. package/dist/component/sparePartOrders.js +243 -0
  104. package/dist/component/sparePartOrders.js.map +1 -0
  105. package/dist/component/spareParts.d.ts +472 -0
  106. package/dist/component/spareParts.d.ts.map +1 -0
  107. package/dist/component/spareParts.js +319 -0
  108. package/dist/component/spareParts.js.map +1 -0
  109. package/dist/component/supplierCategories.d.ts +22 -0
  110. package/dist/component/supplierCategories.d.ts.map +1 -0
  111. package/dist/component/supplierCategories.js +64 -0
  112. package/dist/component/supplierCategories.js.map +1 -0
  113. package/dist/component/suppliers.d.ts +94 -0
  114. package/dist/component/suppliers.d.ts.map +1 -0
  115. package/dist/component/suppliers.js +195 -0
  116. package/dist/component/suppliers.js.map +1 -0
  117. package/dist/component/ticketComments.d.ts +89 -0
  118. package/dist/component/ticketComments.d.ts.map +1 -0
  119. package/dist/component/ticketComments.js +246 -0
  120. package/dist/component/ticketComments.js.map +1 -0
  121. package/dist/component/ticketCustomFields.d.ts +149 -0
  122. package/dist/component/ticketCustomFields.d.ts.map +1 -0
  123. package/dist/component/ticketCustomFields.js +215 -0
  124. package/dist/component/ticketCustomFields.js.map +1 -0
  125. package/dist/component/ticketExport.d.ts +83 -0
  126. package/dist/component/ticketExport.d.ts.map +1 -0
  127. package/dist/component/ticketExport.js +182 -0
  128. package/dist/component/ticketExport.js.map +1 -0
  129. package/dist/component/ticketHistory.d.ts +57 -0
  130. package/dist/component/ticketHistory.d.ts.map +1 -0
  131. package/dist/component/ticketHistory.js +81 -0
  132. package/dist/component/ticketHistory.js.map +1 -0
  133. package/dist/component/ticketMacros.d.ts +141 -0
  134. package/dist/component/ticketMacros.d.ts.map +1 -0
  135. package/dist/component/ticketMacros.js +255 -0
  136. package/dist/component/ticketMacros.js.map +1 -0
  137. package/dist/component/ticketStatuses.d.ts +60 -0
  138. package/dist/component/ticketStatuses.d.ts.map +1 -0
  139. package/dist/component/ticketStatuses.js +110 -0
  140. package/dist/component/ticketStatuses.js.map +1 -0
  141. package/dist/component/ticketTriggers.d.ts +408 -0
  142. package/dist/component/ticketTriggers.d.ts.map +1 -0
  143. package/dist/component/ticketTriggers.js +941 -0
  144. package/dist/component/ticketTriggers.js.map +1 -0
  145. package/dist/component/userProfiles.d.ts +259 -0
  146. package/dist/component/userProfiles.d.ts.map +1 -0
  147. package/dist/component/userProfiles.js +634 -0
  148. package/dist/component/userProfiles.js.map +1 -0
  149. package/dist/component/vendorArticles.d.ts +64 -0
  150. package/dist/component/vendorArticles.d.ts.map +1 -0
  151. package/dist/component/vendorArticles.js +116 -0
  152. package/dist/component/vendorArticles.js.map +1 -0
  153. package/dist/test.d.ts +1302 -0
  154. package/dist/test.d.ts.map +1 -0
  155. package/dist/test.js +7 -0
  156. package/dist/test.js.map +1 -0
  157. package/package.json +71 -0
  158. package/src/client/index.ts +344 -0
  159. package/src/component/_generated/api.ts +110 -0
  160. package/src/component/_generated/component.ts +2460 -0
  161. package/src/component/_generated/dataModel.ts +60 -0
  162. package/src/component/_generated/server.ts +156 -0
  163. package/src/component/apiKeys.ts +229 -0
  164. package/src/component/clinics.ts +136 -0
  165. package/src/component/contracts.ts +136 -0
  166. package/src/component/convex.config.js +2 -0
  167. package/src/component/convex.config.ts +3 -0
  168. package/src/component/crons.ts +18 -0
  169. package/src/component/dashboardStats.ts +141 -0
  170. package/src/component/dashboardStatsCache.ts +145 -0
  171. package/src/component/deviceCategories.ts +280 -0
  172. package/src/component/deviceQuestions.ts +225 -0
  173. package/src/component/deviceRepairHistory.ts +94 -0
  174. package/src/component/deviceStatus.ts +79 -0
  175. package/src/component/devices.ts +645 -0
  176. package/src/component/emailHelpers.ts +38 -0
  177. package/src/component/emails.ts +61 -0
  178. package/src/component/http.ts +231 -0
  179. package/src/component/maintenanceTasks.ts +1003 -0
  180. package/src/component/roles.ts +99 -0
  181. package/src/component/schema.ts +842 -0
  182. package/src/component/slaMonitoring.ts +125 -0
  183. package/src/component/slaRules.ts +231 -0
  184. package/src/component/sparePartOrders.ts +290 -0
  185. package/src/component/spareParts.ts +362 -0
  186. package/src/component/supplierCategories.ts +65 -0
  187. package/src/component/suppliers.ts +234 -0
  188. package/src/component/ticketComments.ts +288 -0
  189. package/src/component/ticketCustomFields.ts +260 -0
  190. package/src/component/ticketExport.ts +220 -0
  191. package/src/component/ticketHistory.ts +106 -0
  192. package/src/component/ticketMacros.ts +291 -0
  193. package/src/component/ticketStatuses.ts +109 -0
  194. package/src/component/ticketTriggers.ts +1152 -0
  195. package/src/component/userProfiles.ts +745 -0
  196. package/src/component/vendorArticles.ts +139 -0
  197. package/src/test.ts +15 -0
@@ -0,0 +1,941 @@
1
+ import { v } from 'convex/values';
2
+ import { query, mutation } from './_generated/server';
3
+ import { api } from './_generated/api';
4
+ export const listAllTriggers = query({
5
+ args: {},
6
+ handler: async (ctx) => {
7
+ const triggers = await ctx.db
8
+ .query('ticket_triggers')
9
+ .collect();
10
+ return triggers.sort((a, b) => a.priority - b.priority);
11
+ },
12
+ });
13
+ export const listActiveTriggers = query({
14
+ args: {},
15
+ handler: async (ctx) => {
16
+ const triggers = await ctx.db
17
+ .query('ticket_triggers')
18
+ .withIndex('by_isActive', (q) => q.eq('isActive', true))
19
+ .collect();
20
+ return triggers.sort((a, b) => a.priority - b.priority);
21
+ },
22
+ });
23
+ export const getTrigger = query({
24
+ args: { triggerId: v.id('ticket_triggers') },
25
+ handler: async (ctx, args) => {
26
+ return await ctx.db.get(args.triggerId);
27
+ },
28
+ });
29
+ export const createTrigger = mutation({
30
+ args: {
31
+ name: v.string(),
32
+ description: v.optional(v.string()),
33
+ priority: v.optional(v.number()),
34
+ conditions: v.object({
35
+ conditionLogic: v.optional(v.union(v.literal('AND'), v.literal('OR'))),
36
+ conditionsList: v.optional(v.array(v.object({
37
+ type: v.union(v.literal('trigger_event'), v.literal('status'), v.literal('category'), v.literal('brand'), v.literal('model'), v.literal('region'), v.literal('priority'), v.literal('custom_field')),
38
+ negated: v.boolean(),
39
+ value: v.union(v.string(), v.array(v.string())),
40
+ nextOperator: v.union(v.literal('AND'), v.literal('OR')),
41
+ customFieldId: v.optional(v.id('ticket_custom_fields')),
42
+ customFieldOperator: v.optional(v.union(v.literal('equals'), v.literal('not_equals'), v.literal('contains'), v.literal('greater_than'), v.literal('less_than'), v.literal('is_empty'), v.literal('is_not_empty'))),
43
+ }))),
44
+ categories: v.optional(v.array(v.string())),
45
+ applyToAllCategories: v.boolean(),
46
+ regions: v.optional(v.array(v.string())),
47
+ applyToAllRegions: v.optional(v.boolean()),
48
+ brands: v.optional(v.array(v.string())),
49
+ brandOperator: v.optional(v.union(v.literal('is'), v.literal('is_not'))),
50
+ models: v.optional(v.array(v.string())),
51
+ modelOperator: v.optional(v.union(v.literal('is'), v.literal('is_not'))),
52
+ statuses: v.optional(v.array(v.string())),
53
+ statusOperator: v.optional(v.union(v.literal('is'), v.literal('is_not'))),
54
+ customFieldConditions: v.optional(v.array(v.object({
55
+ fieldId: v.id('ticket_custom_fields'),
56
+ operator: v.union(v.literal('equals'), v.literal('not_equals'), v.literal('contains'), v.literal('greater_than'), v.literal('less_than'), v.literal('is_empty'), v.literal('is_not_empty')),
57
+ value: v.optional(v.any()),
58
+ }))),
59
+ triggerOn: v.union(v.literal('create'), v.literal('status_change'), v.literal('update'), v.literal('sla_warning'), v.literal('sla_breach'), v.literal('spare_part_order_created'), v.literal('spare_part_order_status_change')),
60
+ sparePartOrderFilters: v.optional(v.object({
61
+ supplierIds: v.optional(v.array(v.id('suppliers'))),
62
+ applyToAllSuppliers: v.optional(v.boolean()),
63
+ statuses: v.optional(v.array(v.string())),
64
+ })),
65
+ }),
66
+ actions: v.object({
67
+ changeStatus: v.optional(v.string()),
68
+ assignSupplier: v.optional(v.id('suppliers')),
69
+ setSlaHours: v.optional(v.number()),
70
+ applySlaRule: v.optional(v.boolean()),
71
+ setPriority: v.optional(v.union(v.literal('low'), v.literal('medium'), v.literal('high'))),
72
+ addNote: v.optional(v.string()),
73
+ sendNotification: v.optional(v.object({
74
+ recipients: v.array(v.object({
75
+ type: v.union(v.literal('admin'), v.literal('supplier'), v.literal('ticket_creator'), v.literal('ticket_assignee'), v.literal('specific_user'), v.literal('specific_supplier')),
76
+ userId: v.optional(v.id('user_profiles')),
77
+ supplierId: v.optional(v.id('suppliers')),
78
+ })),
79
+ message: v.string(),
80
+ })),
81
+ requireAttachment: v.optional(v.boolean()),
82
+ sparePartOrderActions: v.optional(v.object({
83
+ changeOrderStatus: v.optional(v.string()),
84
+ addOrderNote: v.optional(v.string()),
85
+ sendOrderNotification: v.optional(v.object({
86
+ recipients: v.array(v.union(v.literal('admin'), v.literal('supplier'), v.literal('order_creator'))),
87
+ message: v.string(),
88
+ })),
89
+ })),
90
+ }),
91
+ createdBy: v.string(),
92
+ },
93
+ handler: async (ctx, args) => {
94
+ let priority = args.priority;
95
+ if (priority === undefined) {
96
+ const lastTrigger = await ctx.db
97
+ .query('ticket_triggers')
98
+ .withIndex('by_priority')
99
+ .order('desc')
100
+ .first();
101
+ priority = lastTrigger ? lastTrigger.priority + 1 : 0;
102
+ }
103
+ const triggerId = await ctx.db.insert('ticket_triggers', {
104
+ name: args.name,
105
+ description: args.description,
106
+ isActive: true,
107
+ priority,
108
+ conditions: args.conditions,
109
+ actions: args.actions,
110
+ createdAt: Date.now(),
111
+ updatedAt: Date.now(),
112
+ createdBy: args.createdBy,
113
+ });
114
+ return triggerId;
115
+ },
116
+ });
117
+ export const updateTrigger = mutation({
118
+ args: {
119
+ triggerId: v.id('ticket_triggers'),
120
+ name: v.optional(v.string()),
121
+ description: v.optional(v.string()),
122
+ priority: v.optional(v.number()),
123
+ isActive: v.optional(v.boolean()),
124
+ conditions: v.optional(v.object({
125
+ conditionLogic: v.optional(v.union(v.literal('AND'), v.literal('OR'))),
126
+ conditionsList: v.optional(v.array(v.object({
127
+ type: v.union(v.literal('trigger_event'), v.literal('status'), v.literal('category'), v.literal('brand'), v.literal('model'), v.literal('region'), v.literal('priority'), v.literal('custom_field')),
128
+ negated: v.boolean(),
129
+ value: v.union(v.string(), v.array(v.string())),
130
+ nextOperator: v.union(v.literal('AND'), v.literal('OR')),
131
+ customFieldId: v.optional(v.id('ticket_custom_fields')),
132
+ customFieldOperator: v.optional(v.union(v.literal('equals'), v.literal('not_equals'), v.literal('contains'), v.literal('greater_than'), v.literal('less_than'), v.literal('is_empty'), v.literal('is_not_empty'))),
133
+ }))),
134
+ categories: v.optional(v.array(v.string())),
135
+ applyToAllCategories: v.boolean(),
136
+ regions: v.optional(v.array(v.string())),
137
+ applyToAllRegions: v.optional(v.boolean()),
138
+ brands: v.optional(v.array(v.string())),
139
+ brandOperator: v.optional(v.union(v.literal('is'), v.literal('is_not'))),
140
+ models: v.optional(v.array(v.string())),
141
+ modelOperator: v.optional(v.union(v.literal('is'), v.literal('is_not'))),
142
+ statuses: v.optional(v.array(v.string())),
143
+ statusOperator: v.optional(v.union(v.literal('is'), v.literal('is_not'))),
144
+ customFieldConditions: v.optional(v.array(v.object({
145
+ fieldId: v.id('ticket_custom_fields'),
146
+ operator: v.union(v.literal('equals'), v.literal('not_equals'), v.literal('contains'), v.literal('greater_than'), v.literal('less_than'), v.literal('is_empty'), v.literal('is_not_empty')),
147
+ value: v.optional(v.any()),
148
+ }))),
149
+ triggerOn: v.union(v.literal('create'), v.literal('status_change'), v.literal('update'), v.literal('sla_warning'), v.literal('sla_breach'), v.literal('spare_part_order_created'), v.literal('spare_part_order_status_change')),
150
+ sparePartOrderFilters: v.optional(v.object({
151
+ supplierIds: v.optional(v.array(v.id('suppliers'))),
152
+ applyToAllSuppliers: v.optional(v.boolean()),
153
+ statuses: v.optional(v.array(v.string())),
154
+ })),
155
+ })),
156
+ actions: v.optional(v.object({
157
+ changeStatus: v.optional(v.string()),
158
+ assignSupplier: v.optional(v.id('suppliers')),
159
+ setSlaHours: v.optional(v.number()),
160
+ applySlaRule: v.optional(v.boolean()),
161
+ setPriority: v.optional(v.union(v.literal('low'), v.literal('medium'), v.literal('high'))),
162
+ addNote: v.optional(v.string()),
163
+ sendNotification: v.optional(v.object({
164
+ recipients: v.array(v.object({
165
+ type: v.union(v.literal('admin'), v.literal('supplier'), v.literal('ticket_creator'), v.literal('ticket_assignee'), v.literal('specific_user'), v.literal('specific_supplier')),
166
+ userId: v.optional(v.id('user_profiles')),
167
+ supplierId: v.optional(v.id('suppliers')),
168
+ })),
169
+ message: v.string(),
170
+ })),
171
+ requireAttachment: v.optional(v.boolean()),
172
+ sparePartOrderActions: v.optional(v.object({
173
+ changeOrderStatus: v.optional(v.string()),
174
+ addOrderNote: v.optional(v.string()),
175
+ sendOrderNotification: v.optional(v.object({
176
+ recipients: v.array(v.union(v.literal('admin'), v.literal('supplier'), v.literal('order_creator'))),
177
+ message: v.string(),
178
+ })),
179
+ })),
180
+ })),
181
+ },
182
+ handler: async (ctx, args) => {
183
+ const { triggerId, ...updates } = args;
184
+ await ctx.db.patch(triggerId, {
185
+ ...updates,
186
+ updatedAt: Date.now(),
187
+ });
188
+ return triggerId;
189
+ },
190
+ });
191
+ export const deleteTrigger = mutation({
192
+ args: { triggerId: v.id('ticket_triggers') },
193
+ handler: async (ctx, args) => {
194
+ await ctx.db.delete(args.triggerId);
195
+ return { success: true };
196
+ },
197
+ });
198
+ export const toggleTrigger = mutation({
199
+ args: {
200
+ triggerId: v.id('ticket_triggers'),
201
+ isActive: v.boolean(),
202
+ },
203
+ handler: async (ctx, args) => {
204
+ await ctx.db.patch(args.triggerId, {
205
+ isActive: args.isActive,
206
+ updatedAt: Date.now(),
207
+ });
208
+ return { success: true };
209
+ },
210
+ });
211
+ export const duplicateTrigger = mutation({
212
+ args: {
213
+ triggerId: v.id('ticket_triggers'),
214
+ newName: v.optional(v.string()),
215
+ createdBy: v.string(),
216
+ },
217
+ handler: async (ctx, args) => {
218
+ const originalTrigger = await ctx.db.get(args.triggerId);
219
+ if (!originalTrigger) {
220
+ throw new Error('Trigger non trovato');
221
+ }
222
+ const lastTrigger = await ctx.db
223
+ .query('ticket_triggers')
224
+ .withIndex('by_priority')
225
+ .order('desc')
226
+ .first();
227
+ const maxPriority = lastTrigger ? lastTrigger.priority + 1 : 0;
228
+ const duplicateName = args.newName?.trim() || `${originalTrigger.name} (copia)`;
229
+ const newTriggerId = await ctx.db.insert('ticket_triggers', {
230
+ name: duplicateName,
231
+ description: originalTrigger.description,
232
+ isActive: false,
233
+ priority: maxPriority,
234
+ conditions: originalTrigger.conditions,
235
+ actions: originalTrigger.actions,
236
+ createdAt: Date.now(),
237
+ updatedAt: Date.now(),
238
+ createdBy: args.createdBy,
239
+ });
240
+ return newTriggerId;
241
+ },
242
+ });
243
+ export const reorderTriggers = mutation({
244
+ args: {
245
+ triggerOrders: v.array(v.object({
246
+ triggerId: v.id('ticket_triggers'),
247
+ priority: v.number(),
248
+ })),
249
+ },
250
+ handler: async (ctx, args) => {
251
+ for (const { triggerId, priority } of args.triggerOrders) {
252
+ await ctx.db.patch(triggerId, {
253
+ priority,
254
+ updatedAt: Date.now(),
255
+ });
256
+ }
257
+ return { success: true };
258
+ },
259
+ });
260
+ export const executeTriggers = mutation({
261
+ args: {
262
+ ticketId: v.id('maintenance_tasks'),
263
+ triggerOn: v.union(v.literal('create'), v.literal('status_change'), v.literal('update'), v.literal('sla_warning'), v.literal('sla_breach')),
264
+ oldStatus: v.optional(v.string()),
265
+ },
266
+ handler: async (ctx, args) => {
267
+ const ticket = await ctx.db.get(args.ticketId);
268
+ if (!ticket) {
269
+ throw new Error('Ticket not found');
270
+ }
271
+ const device = ticket.deviceId ? await ctx.db.get(ticket.deviceId) : null;
272
+ const clinic = await ctx.db.get(ticket.clinicId);
273
+ const triggers = await ctx.db
274
+ .query('ticket_triggers')
275
+ .withIndex('by_isActive', (q) => q.eq('isActive', true))
276
+ .collect();
277
+ const sortedTriggers = triggers.sort((a, b) => a.priority - b.priority);
278
+ const evaluateCondition = (condition) => {
279
+ const { type, negated, value } = condition;
280
+ let result = false;
281
+ switch (type) {
282
+ case 'trigger_event':
283
+ result = value === args.triggerOn;
284
+ break;
285
+ case 'status':
286
+ const statusValues = Array.isArray(value) ? value : [value];
287
+ result = statusValues.includes(ticket.status);
288
+ break;
289
+ case 'category':
290
+ const categoryValues = Array.isArray(value) ? value : [value];
291
+ result = device ? categoryValues.includes(device.category) : false;
292
+ break;
293
+ case 'brand':
294
+ const brandValues = Array.isArray(value) ? value : [value];
295
+ result = device ? brandValues.includes(device.brand) : false;
296
+ break;
297
+ case 'model':
298
+ const modelValues = Array.isArray(value) ? value : [value];
299
+ result = device ? modelValues.includes(device.model) : false;
300
+ break;
301
+ case 'region':
302
+ const regionValues = Array.isArray(value) ? value : [value];
303
+ const clinicRegion = clinic?.region;
304
+ result = clinicRegion ? regionValues.includes(clinicRegion) : false;
305
+ break;
306
+ case 'priority':
307
+ const priorityValues = Array.isArray(value) ? value : [value];
308
+ result = priorityValues.includes(ticket.priority);
309
+ break;
310
+ case 'custom_field':
311
+ const { customFieldId, customFieldOperator } = condition;
312
+ if (!customFieldId) {
313
+ result = true;
314
+ break;
315
+ }
316
+ const fieldValue = ticket.customFields?.[customFieldId];
317
+ const operator = customFieldOperator || 'equals';
318
+ switch (operator) {
319
+ case 'equals':
320
+ result = fieldValue === value;
321
+ break;
322
+ case 'not_equals':
323
+ result = fieldValue !== value;
324
+ break;
325
+ case 'contains':
326
+ result = fieldValue && String(fieldValue).includes(String(value));
327
+ break;
328
+ case 'greater_than':
329
+ result = fieldValue && Number(fieldValue) > Number(value);
330
+ break;
331
+ case 'less_than':
332
+ result = fieldValue && Number(fieldValue) < Number(value);
333
+ break;
334
+ case 'is_empty':
335
+ result = fieldValue === undefined || fieldValue === null || fieldValue === '';
336
+ break;
337
+ case 'is_not_empty':
338
+ result = fieldValue !== undefined && fieldValue !== null && fieldValue !== '';
339
+ break;
340
+ default:
341
+ result = true;
342
+ }
343
+ break;
344
+ default:
345
+ result = true;
346
+ }
347
+ return negated ? !result : result;
348
+ };
349
+ const evaluateConditionsWithOperators = (conditions) => {
350
+ if (conditions.length === 0)
351
+ return true;
352
+ if (conditions.length === 1)
353
+ return evaluateCondition(conditions[0]);
354
+ const results = conditions.map(c => evaluateCondition(c));
355
+ const andGroups = [[]];
356
+ let currentGroup = 0;
357
+ for (let i = 0; i < conditions.length; i++) {
358
+ andGroups[currentGroup].push(results[i]);
359
+ if (i < conditions.length - 1) {
360
+ const nextOp = conditions[i].nextOperator || 'AND';
361
+ if (nextOp === 'OR') {
362
+ currentGroup++;
363
+ andGroups[currentGroup] = [];
364
+ }
365
+ }
366
+ }
367
+ const groupResults = andGroups.map(group => group.every(r => r));
368
+ return groupResults.some(r => r);
369
+ };
370
+ for (const trigger of sortedTriggers) {
371
+ const triggerConditions = trigger.conditions;
372
+ if (triggerConditions.triggerOn && triggerConditions.triggerOn !== args.triggerOn) {
373
+ continue;
374
+ }
375
+ if (triggerConditions.conditionsList && triggerConditions.conditionsList.length > 0) {
376
+ const shouldExecute = evaluateConditionsWithOperators(triggerConditions.conditionsList);
377
+ if (!shouldExecute) {
378
+ continue;
379
+ }
380
+ }
381
+ else {
382
+ if (trigger.conditions.triggerOn !== args.triggerOn) {
383
+ continue;
384
+ }
385
+ const conditionLogic = trigger.conditions.conditionLogic || 'AND';
386
+ const conditionResults = [];
387
+ let categoryMatch = true;
388
+ if (!trigger.conditions.applyToAllCategories && device) {
389
+ if (!trigger.conditions.categories || !trigger.conditions.categories.includes(device.category)) {
390
+ categoryMatch = false;
391
+ }
392
+ }
393
+ conditionResults.push(categoryMatch);
394
+ let brandMatch = true;
395
+ if (triggerConditions.brands && triggerConditions.brands.length > 0 && device) {
396
+ const brandOperator = triggerConditions.brandOperator || 'is';
397
+ const brandInList = triggerConditions.brands.includes(device.brand);
398
+ if (brandOperator === 'is') {
399
+ brandMatch = brandInList;
400
+ }
401
+ else {
402
+ brandMatch = !brandInList;
403
+ }
404
+ }
405
+ conditionResults.push(brandMatch);
406
+ let modelMatch = true;
407
+ if (triggerConditions.models && triggerConditions.models.length > 0 && device) {
408
+ const modelOperator = triggerConditions.modelOperator || 'is';
409
+ const modelInList = triggerConditions.models.includes(device.model);
410
+ if (modelOperator === 'is') {
411
+ modelMatch = modelInList;
412
+ }
413
+ else {
414
+ modelMatch = !modelInList;
415
+ }
416
+ }
417
+ conditionResults.push(modelMatch);
418
+ let regionMatch = true;
419
+ if (trigger.conditions.applyToAllRegions === false) {
420
+ const clinicRegion = clinic?.region;
421
+ if (!clinicRegion || !trigger.conditions.regions || !trigger.conditions.regions.includes(clinicRegion)) {
422
+ regionMatch = false;
423
+ }
424
+ }
425
+ conditionResults.push(regionMatch);
426
+ let statusMatch = true;
427
+ if (trigger.conditions.statuses && trigger.conditions.statuses.length > 0) {
428
+ const statusOperator = trigger.conditions.statusOperator || 'is';
429
+ const statusInList = trigger.conditions.statuses.includes(ticket.status);
430
+ if (statusOperator === 'is') {
431
+ statusMatch = statusInList;
432
+ }
433
+ else {
434
+ statusMatch = !statusInList;
435
+ }
436
+ }
437
+ conditionResults.push(statusMatch);
438
+ let customFieldsMatch = true;
439
+ if (trigger.conditions.customFieldConditions && trigger.conditions.customFieldConditions.length > 0) {
440
+ for (const condition of trigger.conditions.customFieldConditions) {
441
+ const fieldValue = ticket.customFields?.[condition.fieldId];
442
+ let conditionMet = false;
443
+ switch (condition.operator) {
444
+ case 'equals':
445
+ conditionMet = fieldValue === condition.value;
446
+ break;
447
+ case 'not_equals':
448
+ conditionMet = fieldValue !== condition.value;
449
+ break;
450
+ case 'contains':
451
+ conditionMet = fieldValue && String(fieldValue).includes(String(condition.value));
452
+ break;
453
+ case 'greater_than':
454
+ conditionMet = fieldValue && Number(fieldValue) > Number(condition.value);
455
+ break;
456
+ case 'less_than':
457
+ conditionMet = fieldValue && Number(fieldValue) < Number(condition.value);
458
+ break;
459
+ case 'is_empty':
460
+ conditionMet = fieldValue === undefined || fieldValue === null || fieldValue === '';
461
+ break;
462
+ case 'is_not_empty':
463
+ conditionMet = fieldValue !== undefined && fieldValue !== null && fieldValue !== '';
464
+ break;
465
+ }
466
+ if (!conditionMet) {
467
+ customFieldsMatch = false;
468
+ break;
469
+ }
470
+ }
471
+ }
472
+ conditionResults.push(customFieldsMatch);
473
+ let shouldExecute = false;
474
+ if (conditionLogic === 'OR') {
475
+ shouldExecute = conditionResults.some(result => result === true);
476
+ }
477
+ else {
478
+ shouldExecute = conditionResults.every(result => result === true);
479
+ }
480
+ if (!shouldExecute) {
481
+ continue;
482
+ }
483
+ }
484
+ const updates = {};
485
+ const actions = trigger.actions;
486
+ if (actions.setPriority) {
487
+ updates.priority = actions.setPriority;
488
+ }
489
+ if (trigger.actions.changeStatus) {
490
+ updates.status = trigger.actions.changeStatus;
491
+ }
492
+ if (trigger.actions.assignSupplier) {
493
+ updates.supplierId = trigger.actions.assignSupplier;
494
+ }
495
+ if (actions.applySlaRule && actions.assignSupplier) {
496
+ const priority = actions.setPriority || ticket.priority || 'medium';
497
+ const slaRule = await ctx.db
498
+ .query('sla_rules')
499
+ .withIndex('by_supplierId_priority', (q) => q.eq('supplierId', actions.assignSupplier).eq('priority', priority))
500
+ .first();
501
+ if (slaRule) {
502
+ const slaDeadline = Date.now() + (slaRule.resolutionTimeHours * 60 * 60 * 1000);
503
+ updates.slaDeadline = slaDeadline;
504
+ }
505
+ }
506
+ else if (trigger.actions.setSlaHours) {
507
+ const slaDeadline = Date.now() + (trigger.actions.setSlaHours * 60 * 60 * 1000);
508
+ updates.slaDeadline = slaDeadline;
509
+ }
510
+ if (trigger.actions.addNote) {
511
+ const currentNotes = ticket.notes || '';
512
+ const newNote = `[Trigger: ${trigger.name}] ${trigger.actions.addNote}`;
513
+ updates.notes = currentNotes ? `${currentNotes}\n\n${newNote}` : newNote;
514
+ }
515
+ if (Object.keys(updates).length > 0) {
516
+ updates.updated_at = Date.now();
517
+ await ctx.db.patch(args.ticketId, updates);
518
+ try {
519
+ const actionDescriptions = [];
520
+ if (updates.status)
521
+ actionDescriptions.push(`Stato → ${updates.status}`);
522
+ if (updates.supplierId) {
523
+ const supplier = await ctx.db.get(updates.supplierId);
524
+ actionDescriptions.push(`Assegnato a ${supplier?.name || 'fornitore'}`);
525
+ }
526
+ if (updates.priority)
527
+ actionDescriptions.push(`Priorità → ${updates.priority}`);
528
+ if (updates.slaDeadline)
529
+ actionDescriptions.push(`SLA impostato`);
530
+ await ctx.runMutation(api.ticketHistory.recordHistoryEvent, {
531
+ ticketId: args.ticketId,
532
+ eventType: 'trigger_executed',
533
+ triggerName: trigger.name,
534
+ triggerId: trigger._id,
535
+ oldValue: updates.status ? ticket.status : undefined,
536
+ newValue: updates.status,
537
+ oldSupplierId: updates.supplierId ? ticket.supplierId : undefined,
538
+ newSupplierId: updates.supplierId,
539
+ notes: actionDescriptions.join(', '),
540
+ isSystemAction: true,
541
+ });
542
+ }
543
+ catch (error) {
544
+ console.error('Error recording trigger history:', error);
545
+ }
546
+ }
547
+ }
548
+ return { success: true };
549
+ },
550
+ });
551
+ export const checkAttachmentRequired = query({
552
+ args: {
553
+ deviceId: v.optional(v.id('devices')),
554
+ clinicId: v.optional(v.id('clinics')),
555
+ status: v.optional(v.string()),
556
+ priority: v.optional(v.union(v.literal('low'), v.literal('medium'), v.literal('high'))),
557
+ customFields: v.optional(v.any()),
558
+ },
559
+ handler: async (ctx, args) => {
560
+ try {
561
+ const triggers = await ctx.db
562
+ .query('ticket_triggers')
563
+ .withIndex('by_isActive', (q) => q.eq('isActive', true))
564
+ .collect();
565
+ const attachmentTriggers = triggers.filter((t) => t.actions?.requireAttachment === true);
566
+ if (attachmentTriggers.length === 0) {
567
+ return { required: false, matchingTriggers: [] };
568
+ }
569
+ let device = null;
570
+ let clinic = null;
571
+ try {
572
+ if (args.deviceId) {
573
+ device = await ctx.db.get(args.deviceId);
574
+ }
575
+ }
576
+ catch (e) {
577
+ console.error('Error fetching device:', e);
578
+ }
579
+ try {
580
+ if (args.clinicId) {
581
+ clinic = await ctx.db.get(args.clinicId);
582
+ }
583
+ }
584
+ catch (e) {
585
+ console.error('Error fetching clinic:', e);
586
+ }
587
+ const evaluateCondition = (condition) => {
588
+ const { type, negated, value } = condition;
589
+ let result = false;
590
+ switch (type) {
591
+ case 'trigger_event':
592
+ result = value === 'create';
593
+ break;
594
+ case 'status':
595
+ const statusValues = Array.isArray(value) ? value : [value];
596
+ result = args.status ? statusValues.includes(args.status) : false;
597
+ break;
598
+ case 'category':
599
+ const categoryValues = Array.isArray(value) ? value : [value];
600
+ result = device ? categoryValues.includes(device.category) : false;
601
+ break;
602
+ case 'brand':
603
+ const brandValues = Array.isArray(value) ? value : [value];
604
+ result = device ? brandValues.includes(device.brand) : false;
605
+ break;
606
+ case 'model':
607
+ const modelValues = Array.isArray(value) ? value : [value];
608
+ result = device ? modelValues.includes(device.model) : false;
609
+ break;
610
+ case 'region':
611
+ const regionValues = Array.isArray(value) ? value : [value];
612
+ const clinicRegion = clinic?.region;
613
+ result = clinicRegion ? regionValues.includes(clinicRegion) : false;
614
+ break;
615
+ case 'priority':
616
+ const priorityValues = Array.isArray(value) ? value : [value];
617
+ result = args.priority ? priorityValues.includes(args.priority) : false;
618
+ break;
619
+ case 'custom_field':
620
+ const { customFieldId, customFieldOperator } = condition;
621
+ if (!customFieldId) {
622
+ result = true;
623
+ break;
624
+ }
625
+ const fieldValue = args.customFields?.[customFieldId];
626
+ const operator = customFieldOperator || 'equals';
627
+ switch (operator) {
628
+ case 'equals':
629
+ result = fieldValue === value;
630
+ break;
631
+ case 'not_equals':
632
+ result = fieldValue !== value;
633
+ break;
634
+ case 'contains':
635
+ result = fieldValue && String(fieldValue).includes(String(value));
636
+ break;
637
+ case 'greater_than':
638
+ result = fieldValue && Number(fieldValue) > Number(value);
639
+ break;
640
+ case 'less_than':
641
+ result = fieldValue && Number(fieldValue) < Number(value);
642
+ break;
643
+ case 'is_empty':
644
+ result = fieldValue === undefined || fieldValue === null || fieldValue === '';
645
+ break;
646
+ case 'is_not_empty':
647
+ result = fieldValue !== undefined && fieldValue !== null && fieldValue !== '';
648
+ break;
649
+ default:
650
+ result = true;
651
+ }
652
+ break;
653
+ default:
654
+ result = true;
655
+ }
656
+ return negated ? !result : result;
657
+ };
658
+ const evaluateConditionsWithOperators = (conditions) => {
659
+ if (conditions.length === 0)
660
+ return true;
661
+ if (conditions.length === 1)
662
+ return evaluateCondition(conditions[0]);
663
+ const results = conditions.map(c => evaluateCondition(c));
664
+ const andGroups = [[]];
665
+ let currentGroup = 0;
666
+ for (let i = 0; i < conditions.length; i++) {
667
+ andGroups[currentGroup].push(results[i]);
668
+ if (i < conditions.length - 1) {
669
+ const nextOp = conditions[i].nextOperator || 'AND';
670
+ if (nextOp === 'OR') {
671
+ currentGroup++;
672
+ andGroups[currentGroup] = [];
673
+ }
674
+ }
675
+ }
676
+ const groupResults = andGroups.map(group => group.every(r => r));
677
+ return groupResults.some(r => r);
678
+ };
679
+ const matchingTriggers = [];
680
+ for (const trigger of attachmentTriggers) {
681
+ const triggerConditions = trigger.conditions;
682
+ let matches = false;
683
+ if (triggerConditions.conditionsList && triggerConditions.conditionsList.length > 0) {
684
+ matches = evaluateConditionsWithOperators(triggerConditions.conditionsList);
685
+ }
686
+ else {
687
+ if (triggerConditions.triggerOn !== 'create') {
688
+ continue;
689
+ }
690
+ let categoryMatch = true;
691
+ if (!triggerConditions.applyToAllCategories && device) {
692
+ if (!triggerConditions.categories || !triggerConditions.categories.includes(device.category)) {
693
+ categoryMatch = false;
694
+ }
695
+ }
696
+ let brandMatch = true;
697
+ if (triggerConditions.brands && triggerConditions.brands.length > 0 && device) {
698
+ const brandOperator = triggerConditions.brandOperator || 'is';
699
+ const brandInList = triggerConditions.brands.includes(device.brand);
700
+ brandMatch = brandOperator === 'is' ? brandInList : !brandInList;
701
+ }
702
+ let modelMatch = true;
703
+ if (triggerConditions.models && triggerConditions.models.length > 0 && device) {
704
+ const modelOperator = triggerConditions.modelOperator || 'is';
705
+ const modelInList = triggerConditions.models.includes(device.model);
706
+ modelMatch = modelOperator === 'is' ? modelInList : !modelInList;
707
+ }
708
+ let regionMatch = true;
709
+ if (triggerConditions.applyToAllRegions === false) {
710
+ const clinicRegion = clinic?.region;
711
+ if (!clinicRegion || !triggerConditions.regions || !triggerConditions.regions.includes(clinicRegion)) {
712
+ regionMatch = false;
713
+ }
714
+ }
715
+ matches = categoryMatch && brandMatch && modelMatch && regionMatch;
716
+ }
717
+ if (matches) {
718
+ matchingTriggers.push({
719
+ id: trigger._id,
720
+ name: trigger.name,
721
+ });
722
+ }
723
+ }
724
+ return {
725
+ required: matchingTriggers.length > 0,
726
+ matchingTriggers,
727
+ };
728
+ }
729
+ catch (error) {
730
+ console.error('Error in checkAttachmentRequired:', error);
731
+ return { required: false, matchingTriggers: [] };
732
+ }
733
+ },
734
+ });
735
+ export const simulateTrigger = query({
736
+ args: {
737
+ triggerId: v.id('ticket_triggers'),
738
+ simulationData: v.object({
739
+ triggerOn: v.union(v.literal('create'), v.literal('status_change'), v.literal('update'), v.literal('sla_warning'), v.literal('sla_breach')),
740
+ status: v.optional(v.string()),
741
+ category: v.optional(v.string()),
742
+ brand: v.optional(v.string()),
743
+ model: v.optional(v.string()),
744
+ region: v.optional(v.string()),
745
+ priority: v.optional(v.union(v.literal('low'), v.literal('medium'), v.literal('high'))),
746
+ customFields: v.optional(v.any()),
747
+ }),
748
+ },
749
+ handler: async (ctx, args) => {
750
+ const trigger = await ctx.db.get(args.triggerId);
751
+ if (!trigger) {
752
+ throw new Error('Trigger non trovato');
753
+ }
754
+ const { simulationData } = args;
755
+ const triggerConditions = trigger.conditions;
756
+ const conditionResults = [];
757
+ const evaluateCondition = (condition) => {
758
+ const { type, negated, value } = condition;
759
+ let result = false;
760
+ let actual = null;
761
+ let description = '';
762
+ switch (type) {
763
+ case 'trigger_event':
764
+ actual = simulationData.triggerOn;
765
+ result = value === simulationData.triggerOn;
766
+ description = `Evento: ${value}`;
767
+ break;
768
+ case 'status':
769
+ const statusValues = Array.isArray(value) ? value : [value];
770
+ actual = simulationData.status || '(non specificato)';
771
+ result = simulationData.status ? statusValues.includes(simulationData.status) : false;
772
+ description = `Stato in: [${statusValues.join(', ')}]`;
773
+ break;
774
+ case 'category':
775
+ const categoryValues = Array.isArray(value) ? value : [value];
776
+ actual = simulationData.category || '(non specificato)';
777
+ result = simulationData.category ? categoryValues.includes(simulationData.category) : false;
778
+ description = `Categoria in: [${categoryValues.join(', ')}]`;
779
+ break;
780
+ case 'brand':
781
+ const brandValues = Array.isArray(value) ? value : [value];
782
+ actual = simulationData.brand || '(non specificato)';
783
+ result = simulationData.brand ? brandValues.includes(simulationData.brand) : false;
784
+ description = `Marca in: [${brandValues.join(', ')}]`;
785
+ break;
786
+ case 'model':
787
+ const modelValues = Array.isArray(value) ? value : [value];
788
+ actual = simulationData.model || '(non specificato)';
789
+ result = simulationData.model ? modelValues.includes(simulationData.model) : false;
790
+ description = `Modello in: [${modelValues.join(', ')}]`;
791
+ break;
792
+ case 'region':
793
+ const regionValues = Array.isArray(value) ? value : [value];
794
+ actual = simulationData.region || '(non specificato)';
795
+ result = simulationData.region ? regionValues.includes(simulationData.region) : false;
796
+ description = `Regione in: [${regionValues.join(', ')}]`;
797
+ break;
798
+ case 'priority':
799
+ const priorityValues = Array.isArray(value) ? value : [value];
800
+ actual = simulationData.priority || '(non specificato)';
801
+ result = simulationData.priority ? priorityValues.includes(simulationData.priority) : false;
802
+ description = `Priorità in: [${priorityValues.join(', ')}]`;
803
+ break;
804
+ case 'custom_field':
805
+ const { customFieldId, customFieldOperator } = condition;
806
+ if (!customFieldId) {
807
+ result = true;
808
+ description = 'Campo custom (non configurato)';
809
+ break;
810
+ }
811
+ const fieldValue = simulationData.customFields?.[customFieldId];
812
+ actual = fieldValue ?? '(non specificato)';
813
+ const operator = customFieldOperator || 'equals';
814
+ switch (operator) {
815
+ case 'equals':
816
+ result = fieldValue === value;
817
+ description = `Campo custom = ${value}`;
818
+ break;
819
+ case 'not_equals':
820
+ result = fieldValue !== value;
821
+ description = `Campo custom ≠ ${value}`;
822
+ break;
823
+ case 'contains':
824
+ result = fieldValue && String(fieldValue).includes(String(value));
825
+ description = `Campo custom contiene "${value}"`;
826
+ break;
827
+ case 'greater_than':
828
+ result = fieldValue && Number(fieldValue) > Number(value);
829
+ description = `Campo custom > ${value}`;
830
+ break;
831
+ case 'less_than':
832
+ result = fieldValue && Number(fieldValue) < Number(value);
833
+ description = `Campo custom < ${value}`;
834
+ break;
835
+ case 'is_empty':
836
+ result = fieldValue === undefined || fieldValue === null || fieldValue === '';
837
+ description = 'Campo custom è vuoto';
838
+ break;
839
+ case 'is_not_empty':
840
+ result = fieldValue !== undefined && fieldValue !== null && fieldValue !== '';
841
+ description = 'Campo custom non è vuoto';
842
+ break;
843
+ default:
844
+ result = true;
845
+ description = 'Campo custom (operatore sconosciuto)';
846
+ }
847
+ break;
848
+ default:
849
+ result = true;
850
+ description = `Tipo sconosciuto: ${type}`;
851
+ }
852
+ const finalResult = negated ? !result : result;
853
+ conditionResults.push({
854
+ type,
855
+ expected: value,
856
+ actual,
857
+ negated,
858
+ result: finalResult,
859
+ description: negated ? `NON (${description})` : description,
860
+ });
861
+ return finalResult;
862
+ };
863
+ let wouldTrigger = false;
864
+ if (triggerConditions.conditionsList && triggerConditions.conditionsList.length > 0) {
865
+ const results = triggerConditions.conditionsList.map((c) => evaluateCondition(c));
866
+ const andGroups = [[]];
867
+ let currentGroup = 0;
868
+ for (let i = 0; i < triggerConditions.conditionsList.length; i++) {
869
+ andGroups[currentGroup].push(results[i]);
870
+ if (i < triggerConditions.conditionsList.length - 1) {
871
+ const nextOp = triggerConditions.conditionsList[i].nextOperator || 'AND';
872
+ if (nextOp === 'OR') {
873
+ currentGroup++;
874
+ andGroups[currentGroup] = [];
875
+ }
876
+ }
877
+ }
878
+ const groupResults = andGroups.map(group => group.every(r => r));
879
+ wouldTrigger = groupResults.some(r => r);
880
+ }
881
+ else {
882
+ if (triggerConditions.triggerOn !== simulationData.triggerOn) {
883
+ conditionResults.push({
884
+ type: 'trigger_event',
885
+ expected: triggerConditions.triggerOn,
886
+ actual: simulationData.triggerOn,
887
+ negated: false,
888
+ result: false,
889
+ description: `Evento: ${triggerConditions.triggerOn}`,
890
+ });
891
+ wouldTrigger = false;
892
+ }
893
+ else {
894
+ wouldTrigger = true;
895
+ }
896
+ }
897
+ const actions = trigger.actions;
898
+ const actionDescriptions = [];
899
+ if (actions?.changeStatus) {
900
+ actionDescriptions.push(`📊 Cambierebbe stato a: "${actions.changeStatus}"`);
901
+ }
902
+ if (actions?.assignSupplier) {
903
+ const supplier = await ctx.db.get(actions.assignSupplier);
904
+ actionDescriptions.push(`👤 Assegnerebbe a: ${supplier?.name || 'Fornitore sconosciuto'}`);
905
+ }
906
+ if (actions?.setPriority) {
907
+ const priorityLabels = { low: '🟢 Bassa', medium: '🟡 Media', high: '🟠 Alta' };
908
+ actionDescriptions.push(`⚡ Cambierebbe priorità a: ${priorityLabels[actions.setPriority] || actions.setPriority}`);
909
+ }
910
+ if (actions?.setSlaHours) {
911
+ actionDescriptions.push(`⏱️ Imposterebbe SLA: ${actions.setSlaHours} ore`);
912
+ }
913
+ if (actions?.applySlaRule) {
914
+ actionDescriptions.push(`⏱️ Applicherebbe regola SLA automatica`);
915
+ }
916
+ if (actions?.addNote) {
917
+ actionDescriptions.push(`📝 Aggiungerebbe nota: "${actions.addNote.substring(0, 50)}${actions.addNote.length > 50 ? '...' : ''}"`);
918
+ }
919
+ if (actions?.sendNotification) {
920
+ const recipientTypes = {
921
+ admin: 'Admin',
922
+ supplier: 'Fornitore assegnato',
923
+ ticket_creator: 'Creatore ticket',
924
+ ticket_assignee: 'Assegnatario',
925
+ specific_user: 'Utente specifico',
926
+ specific_supplier: 'Fornitore specifico',
927
+ };
928
+ const recipients = actions.sendNotification.recipients?.map((r) => typeof r === 'string' ? r : recipientTypes[r.type] || r.type).join(', ');
929
+ actionDescriptions.push(`📧 Invierebbe email a: ${recipients}`);
930
+ }
931
+ return {
932
+ triggerId: trigger._id,
933
+ triggerName: trigger.name,
934
+ wouldTrigger,
935
+ conditionResults,
936
+ actionDescriptions,
937
+ isActive: trigger.isActive,
938
+ };
939
+ },
940
+ });
941
+ //# sourceMappingURL=ticketTriggers.js.map