een-api-toolkit 0.3.43 → 0.3.47
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.
- package/.claude/agents/docs-accuracy-reviewer.md +35 -5
- package/.claude/agents/een-automations-agent.md +264 -0
- package/.claude/agents/een-devices-agent.md +5 -7
- package/.claude/agents/een-events-agent.md +30 -16
- package/.claude/agents/een-media-agent.md +12 -15
- package/.claude/agents/een-users-agent.md +2 -2
- package/CHANGELOG.md +6 -8
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +815 -0
- package/dist/index.js +986 -719
- package/dist/index.js.map +1 -1
- package/docs/AI-CONTEXT.md +17 -1
- package/docs/ai-reference/AI-AUTH.md +1 -1
- package/docs/ai-reference/AI-AUTOMATIONS.md +833 -0
- package/docs/ai-reference/AI-DEVICES.md +1 -1
- package/docs/ai-reference/AI-EVENTS.md +1 -1
- package/docs/ai-reference/AI-GROUPING.md +128 -66
- package/docs/ai-reference/AI-MEDIA.md +1 -1
- package/docs/ai-reference/AI-SETUP.md +1 -1
- package/docs/ai-reference/AI-USERS.md +1 -1
- package/examples/vue-automations/.env.example +11 -0
- package/examples/vue-automations/README.md +205 -0
- package/examples/vue-automations/e2e/app.spec.ts +83 -0
- package/examples/vue-automations/e2e/auth.spec.ts +468 -0
- package/examples/vue-automations/index.html +13 -0
- package/examples/vue-automations/package-lock.json +1722 -0
- package/examples/vue-automations/package.json +29 -0
- package/examples/vue-automations/playwright.config.ts +46 -0
- package/examples/vue-automations/src/App.vue +122 -0
- package/examples/vue-automations/src/main.ts +23 -0
- package/examples/vue-automations/src/router/index.ts +61 -0
- package/examples/vue-automations/src/views/Automations.vue +692 -0
- package/examples/vue-automations/src/views/Callback.vue +76 -0
- package/examples/vue-automations/src/views/Home.vue +172 -0
- package/examples/vue-automations/src/views/Login.vue +33 -0
- package/examples/vue-automations/src/views/Logout.vue +66 -0
- package/examples/vue-automations/src/vite-env.d.ts +1 -0
- package/examples/vue-automations/tsconfig.json +21 -0
- package/examples/vue-automations/tsconfig.node.json +10 -0
- package/examples/vue-automations/vite.config.ts +12 -0
- package/examples/vue-event-subscriptions/e2e/auth.spec.ts +8 -12
- package/package.json +1 -1
- package/scripts/setup-agents.ts +38 -19
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, computed, onMounted } from 'vue'
|
|
3
|
+
import {
|
|
4
|
+
listEventAlertConditionRules,
|
|
5
|
+
listAlertConditionRules,
|
|
6
|
+
listAlertActionRules,
|
|
7
|
+
listAlertActions,
|
|
8
|
+
type EventAlertConditionRule,
|
|
9
|
+
type AlertConditionRule,
|
|
10
|
+
type AlertActionRule,
|
|
11
|
+
type AutomationAlertAction,
|
|
12
|
+
type EenError
|
|
13
|
+
} from 'een-api-toolkit'
|
|
14
|
+
|
|
15
|
+
// Active tab
|
|
16
|
+
type TabName = 'eventAlertRules' | 'conditionRules' | 'actionRules' | 'actions'
|
|
17
|
+
const activeTab = ref<TabName>('eventAlertRules')
|
|
18
|
+
|
|
19
|
+
// Modal state
|
|
20
|
+
type SelectedItem = EventAlertConditionRule | AlertConditionRule | AlertActionRule | AutomationAlertAction
|
|
21
|
+
const showModal = ref(false)
|
|
22
|
+
const selectedItem = ref<SelectedItem | null>(null)
|
|
23
|
+
const selectedItemTitle = ref('')
|
|
24
|
+
|
|
25
|
+
function openModal(item: SelectedItem, title: string) {
|
|
26
|
+
selectedItem.value = item
|
|
27
|
+
selectedItemTitle.value = title
|
|
28
|
+
showModal.value = true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function closeModal() {
|
|
32
|
+
showModal.value = false
|
|
33
|
+
selectedItem.value = null
|
|
34
|
+
selectedItemTitle.value = ''
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatJson(obj: unknown): string {
|
|
38
|
+
return JSON.stringify(obj, null, 2)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Event Alert Condition Rules
|
|
42
|
+
const eventAlertRules = ref<EventAlertConditionRule[]>([])
|
|
43
|
+
const eventAlertRulesLoading = ref(false)
|
|
44
|
+
const eventAlertRulesError = ref<EenError | null>(null)
|
|
45
|
+
const eventAlertRulesNextToken = ref<string | undefined>(undefined)
|
|
46
|
+
const hasMoreEventAlertRules = computed(() => !!eventAlertRulesNextToken.value)
|
|
47
|
+
|
|
48
|
+
// Alert Condition Rules
|
|
49
|
+
const conditionRules = ref<AlertConditionRule[]>([])
|
|
50
|
+
const conditionRulesLoading = ref(false)
|
|
51
|
+
const conditionRulesError = ref<EenError | null>(null)
|
|
52
|
+
const conditionRulesNextToken = ref<string | undefined>(undefined)
|
|
53
|
+
const hasMoreConditionRules = computed(() => !!conditionRulesNextToken.value)
|
|
54
|
+
|
|
55
|
+
// Alert Action Rules
|
|
56
|
+
const actionRules = ref<AlertActionRule[]>([])
|
|
57
|
+
const actionRulesLoading = ref(false)
|
|
58
|
+
const actionRulesError = ref<EenError | null>(null)
|
|
59
|
+
const actionRulesNextToken = ref<string | undefined>(undefined)
|
|
60
|
+
const hasMoreActionRules = computed(() => !!actionRulesNextToken.value)
|
|
61
|
+
|
|
62
|
+
// Alert Actions
|
|
63
|
+
const actions = ref<AutomationAlertAction[]>([])
|
|
64
|
+
const actionsLoading = ref(false)
|
|
65
|
+
const actionsError = ref<EenError | null>(null)
|
|
66
|
+
const actionsNextToken = ref<string | undefined>(undefined)
|
|
67
|
+
const hasMoreActions = computed(() => !!actionsNextToken.value)
|
|
68
|
+
|
|
69
|
+
// Filter state
|
|
70
|
+
const enabledFilter = ref<'all' | 'enabled' | 'disabled'>('all')
|
|
71
|
+
|
|
72
|
+
// Fetch functions
|
|
73
|
+
async function fetchEventAlertRules(append = false) {
|
|
74
|
+
eventAlertRulesLoading.value = true
|
|
75
|
+
eventAlertRulesError.value = null
|
|
76
|
+
|
|
77
|
+
const params: Parameters<typeof listEventAlertConditionRules>[0] = {
|
|
78
|
+
pageSize: 10,
|
|
79
|
+
...(append && eventAlertRulesNextToken.value ? { pageToken: eventAlertRulesNextToken.value } : {}),
|
|
80
|
+
...(enabledFilter.value !== 'all' ? { enabled: enabledFilter.value === 'enabled' } : {})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const result = await listEventAlertConditionRules(params)
|
|
84
|
+
|
|
85
|
+
if (result.error) {
|
|
86
|
+
eventAlertRulesError.value = result.error
|
|
87
|
+
if (!append) eventAlertRules.value = []
|
|
88
|
+
eventAlertRulesNextToken.value = undefined
|
|
89
|
+
} else {
|
|
90
|
+
if (append) {
|
|
91
|
+
eventAlertRules.value = [...eventAlertRules.value, ...result.data.results]
|
|
92
|
+
} else {
|
|
93
|
+
eventAlertRules.value = result.data.results
|
|
94
|
+
}
|
|
95
|
+
eventAlertRulesNextToken.value = result.data.nextPageToken
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
eventAlertRulesLoading.value = false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function fetchConditionRules(append = false) {
|
|
102
|
+
conditionRulesLoading.value = true
|
|
103
|
+
conditionRulesError.value = null
|
|
104
|
+
|
|
105
|
+
const params: Parameters<typeof listAlertConditionRules>[0] = {
|
|
106
|
+
pageSize: 10,
|
|
107
|
+
include: ['actions', 'insights'],
|
|
108
|
+
...(append && conditionRulesNextToken.value ? { pageToken: conditionRulesNextToken.value } : {}),
|
|
109
|
+
...(enabledFilter.value !== 'all' ? { enabled: enabledFilter.value === 'enabled' } : {})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const result = await listAlertConditionRules(params)
|
|
113
|
+
|
|
114
|
+
if (result.error) {
|
|
115
|
+
conditionRulesError.value = result.error
|
|
116
|
+
if (!append) conditionRules.value = []
|
|
117
|
+
conditionRulesNextToken.value = undefined
|
|
118
|
+
} else {
|
|
119
|
+
if (append) {
|
|
120
|
+
conditionRules.value = [...conditionRules.value, ...result.data.results]
|
|
121
|
+
} else {
|
|
122
|
+
conditionRules.value = result.data.results
|
|
123
|
+
}
|
|
124
|
+
conditionRulesNextToken.value = result.data.nextPageToken
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
conditionRulesLoading.value = false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function fetchActionRules(append = false) {
|
|
131
|
+
actionRulesLoading.value = true
|
|
132
|
+
actionRulesError.value = null
|
|
133
|
+
|
|
134
|
+
const params: Parameters<typeof listAlertActionRules>[0] = {
|
|
135
|
+
pageSize: 10,
|
|
136
|
+
...(append && actionRulesNextToken.value ? { pageToken: actionRulesNextToken.value } : {}),
|
|
137
|
+
...(enabledFilter.value !== 'all' ? { enabled: enabledFilter.value === 'enabled' } : {})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const result = await listAlertActionRules(params)
|
|
141
|
+
|
|
142
|
+
if (result.error) {
|
|
143
|
+
actionRulesError.value = result.error
|
|
144
|
+
if (!append) actionRules.value = []
|
|
145
|
+
actionRulesNextToken.value = undefined
|
|
146
|
+
} else {
|
|
147
|
+
if (append) {
|
|
148
|
+
actionRules.value = [...actionRules.value, ...result.data.results]
|
|
149
|
+
} else {
|
|
150
|
+
actionRules.value = result.data.results
|
|
151
|
+
}
|
|
152
|
+
actionRulesNextToken.value = result.data.nextPageToken
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
actionRulesLoading.value = false
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function fetchActions(append = false) {
|
|
159
|
+
actionsLoading.value = true
|
|
160
|
+
actionsError.value = null
|
|
161
|
+
|
|
162
|
+
const params: Parameters<typeof listAlertActions>[0] = {
|
|
163
|
+
pageSize: 10,
|
|
164
|
+
...(append && actionsNextToken.value ? { pageToken: actionsNextToken.value } : {}),
|
|
165
|
+
...(enabledFilter.value !== 'all' ? { enabled: enabledFilter.value === 'enabled' } : {})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const result = await listAlertActions(params)
|
|
169
|
+
|
|
170
|
+
if (result.error) {
|
|
171
|
+
actionsError.value = result.error
|
|
172
|
+
if (!append) actions.value = []
|
|
173
|
+
actionsNextToken.value = undefined
|
|
174
|
+
} else {
|
|
175
|
+
if (append) {
|
|
176
|
+
actions.value = [...actions.value, ...result.data.results]
|
|
177
|
+
} else {
|
|
178
|
+
actions.value = result.data.results
|
|
179
|
+
}
|
|
180
|
+
actionsNextToken.value = result.data.nextPageToken
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
actionsLoading.value = false
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function refreshCurrentTab() {
|
|
187
|
+
switch (activeTab.value) {
|
|
188
|
+
case 'eventAlertRules':
|
|
189
|
+
fetchEventAlertRules()
|
|
190
|
+
break
|
|
191
|
+
case 'conditionRules':
|
|
192
|
+
fetchConditionRules()
|
|
193
|
+
break
|
|
194
|
+
case 'actionRules':
|
|
195
|
+
fetchActionRules()
|
|
196
|
+
break
|
|
197
|
+
case 'actions':
|
|
198
|
+
fetchActions()
|
|
199
|
+
break
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function loadMore() {
|
|
204
|
+
switch (activeTab.value) {
|
|
205
|
+
case 'eventAlertRules':
|
|
206
|
+
fetchEventAlertRules(true)
|
|
207
|
+
break
|
|
208
|
+
case 'conditionRules':
|
|
209
|
+
fetchConditionRules(true)
|
|
210
|
+
break
|
|
211
|
+
case 'actionRules':
|
|
212
|
+
fetchActionRules(true)
|
|
213
|
+
break
|
|
214
|
+
case 'actions':
|
|
215
|
+
fetchActions(true)
|
|
216
|
+
break
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function switchTab(tab: TabName) {
|
|
221
|
+
activeTab.value = tab
|
|
222
|
+
// Fetch data for the tab if not loaded
|
|
223
|
+
switch (tab) {
|
|
224
|
+
case 'eventAlertRules':
|
|
225
|
+
if (eventAlertRules.value.length === 0 && !eventAlertRulesLoading.value) {
|
|
226
|
+
fetchEventAlertRules()
|
|
227
|
+
}
|
|
228
|
+
break
|
|
229
|
+
case 'conditionRules':
|
|
230
|
+
if (conditionRules.value.length === 0 && !conditionRulesLoading.value) {
|
|
231
|
+
fetchConditionRules()
|
|
232
|
+
}
|
|
233
|
+
break
|
|
234
|
+
case 'actionRules':
|
|
235
|
+
if (actionRules.value.length === 0 && !actionRulesLoading.value) {
|
|
236
|
+
fetchActionRules()
|
|
237
|
+
}
|
|
238
|
+
break
|
|
239
|
+
case 'actions':
|
|
240
|
+
if (actions.value.length === 0 && !actionsLoading.value) {
|
|
241
|
+
fetchActions()
|
|
242
|
+
}
|
|
243
|
+
break
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const isLoading = computed(() => {
|
|
248
|
+
switch (activeTab.value) {
|
|
249
|
+
case 'eventAlertRules':
|
|
250
|
+
return eventAlertRulesLoading.value
|
|
251
|
+
case 'conditionRules':
|
|
252
|
+
return conditionRulesLoading.value
|
|
253
|
+
case 'actionRules':
|
|
254
|
+
return actionRulesLoading.value
|
|
255
|
+
case 'actions':
|
|
256
|
+
return actionsLoading.value
|
|
257
|
+
default:
|
|
258
|
+
return false
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const hasMore = computed(() => {
|
|
263
|
+
switch (activeTab.value) {
|
|
264
|
+
case 'eventAlertRules':
|
|
265
|
+
return hasMoreEventAlertRules.value
|
|
266
|
+
case 'conditionRules':
|
|
267
|
+
return hasMoreConditionRules.value
|
|
268
|
+
case 'actionRules':
|
|
269
|
+
return hasMoreActionRules.value
|
|
270
|
+
case 'actions':
|
|
271
|
+
return hasMoreActions.value
|
|
272
|
+
default:
|
|
273
|
+
return false
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
onMounted(() => {
|
|
278
|
+
fetchEventAlertRules()
|
|
279
|
+
})
|
|
280
|
+
</script>
|
|
281
|
+
|
|
282
|
+
<template>
|
|
283
|
+
<div class="automations">
|
|
284
|
+
<div class="header">
|
|
285
|
+
<h2>Automations</h2>
|
|
286
|
+
<div class="controls">
|
|
287
|
+
<select v-model="enabledFilter" @change="refreshCurrentTab" data-testid="enabled-filter">
|
|
288
|
+
<option value="all">All</option>
|
|
289
|
+
<option value="enabled">Enabled Only</option>
|
|
290
|
+
<option value="disabled">Disabled Only</option>
|
|
291
|
+
</select>
|
|
292
|
+
<button @click="refreshCurrentTab" :disabled="isLoading" data-testid="refresh-button">
|
|
293
|
+
{{ isLoading ? 'Loading...' : 'Refresh' }}
|
|
294
|
+
</button>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div class="tabs">
|
|
299
|
+
<button
|
|
300
|
+
:class="{ active: activeTab === 'eventAlertRules' }"
|
|
301
|
+
@click="switchTab('eventAlertRules')"
|
|
302
|
+
data-testid="tab-event-alert-rules"
|
|
303
|
+
>
|
|
304
|
+
Event Alert Rules
|
|
305
|
+
</button>
|
|
306
|
+
<button
|
|
307
|
+
:class="{ active: activeTab === 'conditionRules' }"
|
|
308
|
+
@click="switchTab('conditionRules')"
|
|
309
|
+
data-testid="tab-condition-rules"
|
|
310
|
+
>
|
|
311
|
+
Condition Rules
|
|
312
|
+
</button>
|
|
313
|
+
<button
|
|
314
|
+
:class="{ active: activeTab === 'actionRules' }"
|
|
315
|
+
@click="switchTab('actionRules')"
|
|
316
|
+
data-testid="tab-action-rules"
|
|
317
|
+
>
|
|
318
|
+
Action Rules
|
|
319
|
+
</button>
|
|
320
|
+
<button
|
|
321
|
+
:class="{ active: activeTab === 'actions' }"
|
|
322
|
+
@click="switchTab('actions')"
|
|
323
|
+
data-testid="tab-actions"
|
|
324
|
+
>
|
|
325
|
+
Actions
|
|
326
|
+
</button>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<!-- Event Alert Condition Rules -->
|
|
330
|
+
<div v-if="activeTab === 'eventAlertRules'" class="tab-content" data-testid="event-alert-rules-content">
|
|
331
|
+
<div v-if="eventAlertRulesLoading && eventAlertRules.length === 0" class="loading">
|
|
332
|
+
Loading event alert condition rules...
|
|
333
|
+
</div>
|
|
334
|
+
<div v-else-if="eventAlertRulesError" class="error">
|
|
335
|
+
Error: {{ eventAlertRulesError.message }}
|
|
336
|
+
</div>
|
|
337
|
+
<div v-else>
|
|
338
|
+
<table v-if="eventAlertRules.length > 0" data-testid="event-alert-rules-table">
|
|
339
|
+
<thead>
|
|
340
|
+
<tr>
|
|
341
|
+
<th>Name</th>
|
|
342
|
+
<th>Priority</th>
|
|
343
|
+
<th>Enabled</th>
|
|
344
|
+
<th>Output Types</th>
|
|
345
|
+
</tr>
|
|
346
|
+
</thead>
|
|
347
|
+
<tbody>
|
|
348
|
+
<tr
|
|
349
|
+
v-for="rule in eventAlertRules"
|
|
350
|
+
:key="rule.id"
|
|
351
|
+
class="clickable-row"
|
|
352
|
+
@click="openModal(rule, 'Event Alert Condition Rule')"
|
|
353
|
+
>
|
|
354
|
+
<td>{{ rule.name }}</td>
|
|
355
|
+
<td>{{ rule.priority }}</td>
|
|
356
|
+
<td>
|
|
357
|
+
<span :class="rule.enabled ? 'enabled' : 'disabled'">
|
|
358
|
+
{{ rule.enabled ? 'Yes' : 'No' }}
|
|
359
|
+
</span>
|
|
360
|
+
</td>
|
|
361
|
+
<td>{{ rule.outputAlertTypes?.join(', ') || '-' }}</td>
|
|
362
|
+
</tr>
|
|
363
|
+
</tbody>
|
|
364
|
+
</table>
|
|
365
|
+
<p v-else class="no-data">No event alert condition rules found.</p>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
<!-- Alert Condition Rules -->
|
|
370
|
+
<div v-if="activeTab === 'conditionRules'" class="tab-content" data-testid="condition-rules-content">
|
|
371
|
+
<div v-if="conditionRulesLoading && conditionRules.length === 0" class="loading">
|
|
372
|
+
Loading alert condition rules...
|
|
373
|
+
</div>
|
|
374
|
+
<div v-else-if="conditionRulesError" class="error">
|
|
375
|
+
Error: {{ conditionRulesError.message }}
|
|
376
|
+
</div>
|
|
377
|
+
<div v-else>
|
|
378
|
+
<table v-if="conditionRules.length > 0" data-testid="condition-rules-table">
|
|
379
|
+
<thead>
|
|
380
|
+
<tr>
|
|
381
|
+
<th>Name</th>
|
|
382
|
+
<th>Type</th>
|
|
383
|
+
<th>Priority</th>
|
|
384
|
+
<th>Enabled</th>
|
|
385
|
+
<th>Actions</th>
|
|
386
|
+
</tr>
|
|
387
|
+
</thead>
|
|
388
|
+
<tbody>
|
|
389
|
+
<tr
|
|
390
|
+
v-for="rule in conditionRules"
|
|
391
|
+
:key="rule.id"
|
|
392
|
+
class="clickable-row"
|
|
393
|
+
@click="openModal(rule, 'Alert Condition Rule')"
|
|
394
|
+
>
|
|
395
|
+
<td>{{ rule.name }}</td>
|
|
396
|
+
<td>{{ rule.type }}</td>
|
|
397
|
+
<td>{{ rule.priority }}</td>
|
|
398
|
+
<td>
|
|
399
|
+
<span :class="rule.enabled ? 'enabled' : 'disabled'">
|
|
400
|
+
{{ rule.enabled ? 'Yes' : 'No' }}
|
|
401
|
+
</span>
|
|
402
|
+
</td>
|
|
403
|
+
<td>{{ rule.actions?.length ?? 0 }}</td>
|
|
404
|
+
</tr>
|
|
405
|
+
</tbody>
|
|
406
|
+
</table>
|
|
407
|
+
<p v-else class="no-data">No alert condition rules found.</p>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
<!-- Alert Action Rules -->
|
|
412
|
+
<div v-if="activeTab === 'actionRules'" class="tab-content" data-testid="action-rules-content">
|
|
413
|
+
<div v-if="actionRulesLoading && actionRules.length === 0" class="loading">
|
|
414
|
+
Loading alert action rules...
|
|
415
|
+
</div>
|
|
416
|
+
<div v-else-if="actionRulesError" class="error">
|
|
417
|
+
Error: {{ actionRulesError.message }}
|
|
418
|
+
</div>
|
|
419
|
+
<div v-else>
|
|
420
|
+
<table v-if="actionRules.length > 0" data-testid="action-rules-table">
|
|
421
|
+
<thead>
|
|
422
|
+
<tr>
|
|
423
|
+
<th>Name</th>
|
|
424
|
+
<th>Enabled</th>
|
|
425
|
+
<th>Alert Types</th>
|
|
426
|
+
<th>Actions</th>
|
|
427
|
+
</tr>
|
|
428
|
+
</thead>
|
|
429
|
+
<tbody>
|
|
430
|
+
<tr
|
|
431
|
+
v-for="rule in actionRules"
|
|
432
|
+
:key="rule.id"
|
|
433
|
+
class="clickable-row"
|
|
434
|
+
@click="openModal(rule, 'Alert Action Rule')"
|
|
435
|
+
>
|
|
436
|
+
<td>{{ rule.name }}</td>
|
|
437
|
+
<td>
|
|
438
|
+
<span :class="rule.enabled ? 'enabled' : 'disabled'">
|
|
439
|
+
{{ rule.enabled ? 'Yes' : 'No' }}
|
|
440
|
+
</span>
|
|
441
|
+
</td>
|
|
442
|
+
<td>{{ rule.alertTypes?.join(', ') || '-' }}</td>
|
|
443
|
+
<td>{{ rule.alertActionIds?.length ?? 0 }}</td>
|
|
444
|
+
</tr>
|
|
445
|
+
</tbody>
|
|
446
|
+
</table>
|
|
447
|
+
<p v-else class="no-data">No alert action rules found.</p>
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
|
|
451
|
+
<!-- Alert Actions -->
|
|
452
|
+
<div v-if="activeTab === 'actions'" class="tab-content" data-testid="actions-content">
|
|
453
|
+
<div v-if="actionsLoading && actions.length === 0" class="loading">
|
|
454
|
+
Loading alert actions...
|
|
455
|
+
</div>
|
|
456
|
+
<div v-else-if="actionsError" class="error">
|
|
457
|
+
Error: {{ actionsError.message }}
|
|
458
|
+
</div>
|
|
459
|
+
<div v-else>
|
|
460
|
+
<table v-if="actions.length > 0" data-testid="actions-table">
|
|
461
|
+
<thead>
|
|
462
|
+
<tr>
|
|
463
|
+
<th>Name</th>
|
|
464
|
+
<th>Type</th>
|
|
465
|
+
<th>Enabled</th>
|
|
466
|
+
</tr>
|
|
467
|
+
</thead>
|
|
468
|
+
<tbody>
|
|
469
|
+
<tr
|
|
470
|
+
v-for="action in actions"
|
|
471
|
+
:key="action.id"
|
|
472
|
+
class="clickable-row"
|
|
473
|
+
@click="openModal(action, 'Alert Action')"
|
|
474
|
+
>
|
|
475
|
+
<td>{{ action.name }}</td>
|
|
476
|
+
<td>{{ action.type }}</td>
|
|
477
|
+
<td>
|
|
478
|
+
<span :class="action.enabled ? 'enabled' : 'disabled'">
|
|
479
|
+
{{ action.enabled ? 'Yes' : 'No' }}
|
|
480
|
+
</span>
|
|
481
|
+
</td>
|
|
482
|
+
</tr>
|
|
483
|
+
</tbody>
|
|
484
|
+
</table>
|
|
485
|
+
<p v-else class="no-data">No alert actions found.</p>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<!-- Pagination -->
|
|
490
|
+
<div v-if="hasMore" class="pagination">
|
|
491
|
+
<button @click="loadMore" :disabled="isLoading" data-testid="load-more-button">
|
|
492
|
+
{{ isLoading ? 'Loading...' : 'Load More' }}
|
|
493
|
+
</button>
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
<!-- Detail Modal -->
|
|
497
|
+
<div v-if="showModal" class="modal-overlay" @click.self="closeModal" data-testid="modal-overlay">
|
|
498
|
+
<div class="modal" data-testid="detail-modal">
|
|
499
|
+
<div class="modal-header">
|
|
500
|
+
<h3>{{ selectedItemTitle }}</h3>
|
|
501
|
+
<button class="modal-close" @click="closeModal" data-testid="modal-close">×</button>
|
|
502
|
+
</div>
|
|
503
|
+
<div class="modal-body">
|
|
504
|
+
<pre class="json-content" data-testid="modal-json">{{ formatJson(selectedItem) }}</pre>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
</div>
|
|
508
|
+
</div>
|
|
509
|
+
</template>
|
|
510
|
+
|
|
511
|
+
<style scoped>
|
|
512
|
+
.automations {
|
|
513
|
+
max-width: 1000px;
|
|
514
|
+
margin: 0 auto;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.header {
|
|
518
|
+
display: flex;
|
|
519
|
+
justify-content: space-between;
|
|
520
|
+
align-items: center;
|
|
521
|
+
margin-bottom: 20px;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.controls {
|
|
525
|
+
display: flex;
|
|
526
|
+
gap: 10px;
|
|
527
|
+
align-items: center;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.controls select {
|
|
531
|
+
padding: 8px 12px;
|
|
532
|
+
border: 1px solid #ddd;
|
|
533
|
+
border-radius: 4px;
|
|
534
|
+
font-size: 1rem;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.tabs {
|
|
538
|
+
display: flex;
|
|
539
|
+
gap: 0;
|
|
540
|
+
margin-bottom: 20px;
|
|
541
|
+
border-bottom: 2px solid #eee;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.tabs button {
|
|
545
|
+
padding: 10px 20px;
|
|
546
|
+
background: none;
|
|
547
|
+
border: none;
|
|
548
|
+
border-bottom: 2px solid transparent;
|
|
549
|
+
margin-bottom: -2px;
|
|
550
|
+
cursor: pointer;
|
|
551
|
+
color: #666;
|
|
552
|
+
font-size: 0.95rem;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.tabs button:hover {
|
|
556
|
+
color: #333;
|
|
557
|
+
background: #f5f5f5;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.tabs button.active {
|
|
561
|
+
color: #4a90a4;
|
|
562
|
+
border-bottom-color: #4a90a4;
|
|
563
|
+
font-weight: 600;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.tab-content {
|
|
567
|
+
min-height: 200px;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
table {
|
|
571
|
+
width: 100%;
|
|
572
|
+
border-collapse: collapse;
|
|
573
|
+
background: #fff;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
th,
|
|
577
|
+
td {
|
|
578
|
+
padding: 12px;
|
|
579
|
+
text-align: left;
|
|
580
|
+
border-bottom: 1px solid #eee;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
th {
|
|
584
|
+
background: #f5f5f5;
|
|
585
|
+
font-weight: 600;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.enabled {
|
|
589
|
+
color: #27ae60;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.disabled {
|
|
593
|
+
color: #e74c3c;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.no-data {
|
|
597
|
+
text-align: center;
|
|
598
|
+
color: #666;
|
|
599
|
+
padding: 40px;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.pagination {
|
|
603
|
+
margin-top: 20px;
|
|
604
|
+
text-align: center;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.loading {
|
|
608
|
+
text-align: center;
|
|
609
|
+
color: #666;
|
|
610
|
+
padding: 40px;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.clickable-row {
|
|
614
|
+
cursor: pointer;
|
|
615
|
+
transition: background-color 0.15s ease;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.clickable-row:hover {
|
|
619
|
+
background-color: #f0f7fa;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.modal-overlay {
|
|
623
|
+
position: fixed;
|
|
624
|
+
top: 0;
|
|
625
|
+
left: 0;
|
|
626
|
+
right: 0;
|
|
627
|
+
bottom: 0;
|
|
628
|
+
background: rgba(0, 0, 0, 0.5);
|
|
629
|
+
display: flex;
|
|
630
|
+
align-items: center;
|
|
631
|
+
justify-content: center;
|
|
632
|
+
z-index: 1000;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.modal {
|
|
636
|
+
background: #fff;
|
|
637
|
+
border-radius: 8px;
|
|
638
|
+
width: 80%;
|
|
639
|
+
max-height: 80vh;
|
|
640
|
+
display: flex;
|
|
641
|
+
flex-direction: column;
|
|
642
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.modal-header {
|
|
646
|
+
display: flex;
|
|
647
|
+
justify-content: space-between;
|
|
648
|
+
align-items: center;
|
|
649
|
+
padding: 16px 20px;
|
|
650
|
+
border-bottom: 1px solid #eee;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.modal-header h3 {
|
|
654
|
+
margin: 0;
|
|
655
|
+
font-size: 1.1rem;
|
|
656
|
+
color: #333;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.modal-close {
|
|
660
|
+
background: none;
|
|
661
|
+
border: none;
|
|
662
|
+
font-size: 1.5rem;
|
|
663
|
+
cursor: pointer;
|
|
664
|
+
color: #666;
|
|
665
|
+
padding: 0;
|
|
666
|
+
line-height: 1;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.modal-close:hover {
|
|
670
|
+
color: #333;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.modal-body {
|
|
674
|
+
padding: 20px;
|
|
675
|
+
overflow: auto;
|
|
676
|
+
flex: 1;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.json-content {
|
|
680
|
+
background: #f8f9fa;
|
|
681
|
+
padding: 16px;
|
|
682
|
+
border-radius: 4px;
|
|
683
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
684
|
+
font-size: 0.85rem;
|
|
685
|
+
line-height: 1.5;
|
|
686
|
+
overflow-x: auto;
|
|
687
|
+
white-space: pre-wrap;
|
|
688
|
+
word-wrap: break-word;
|
|
689
|
+
margin: 0;
|
|
690
|
+
color: #333;
|
|
691
|
+
}
|
|
692
|
+
</style>
|