|
161 | 161 |
|
162 | 162 | <p class="text-gray-600 mb-6"> |
163 | 163 | <span v-if="bulkActionModal.action === 'activate'"> |
164 | | - Are you sure you want to <strong>activate</strong> all {{ sortedAndFilteredRules.length }} filtered rules? |
| 164 | + <template v-if="getRulesCountForAction('activate') > 0"> |
| 165 | + Are you sure you want to <strong>activate</strong> {{ getRulesCountForAction('activate') }} rule{{ getRulesCountForAction('activate') > 1 ? 's' : '' }}? |
| 166 | + <span v-if="getRulesCountForAction('activate') < sortedAndFilteredRules.length" class="text-sm text-gray-500"> |
| 167 | + <br>({{ sortedAndFilteredRules.length - getRulesCountForAction('activate') }} rule{{ sortedAndFilteredRules.length - getRulesCountForAction('activate') > 1 ? 's are' : ' is' }} already active) |
| 168 | + </span> |
| 169 | + </template> |
| 170 | + <template v-else> |
| 171 | + All {{ sortedAndFilteredRules.length }} filtered rule{{ sortedAndFilteredRules.length > 1 ? 's are' : ' is' }} already <strong>active</strong>. |
| 172 | + </template> |
165 | 173 | </span> |
166 | 174 | <span v-else-if="bulkActionModal.action === 'deactivate'"> |
167 | | - Are you sure you want to <strong>deactivate</strong> all {{ sortedAndFilteredRules.length }} filtered rules? |
| 175 | + <template v-if="getRulesCountForAction('deactivate') > 0"> |
| 176 | + Are you sure you want to <strong>deactivate</strong> {{ getRulesCountForAction('deactivate') }} rule{{ getRulesCountForAction('deactivate') > 1 ? 's' : '' }}? |
| 177 | + <span v-if="getRulesCountForAction('deactivate') < sortedAndFilteredRules.length" class="text-sm text-gray-500"> |
| 178 | + <br>({{ sortedAndFilteredRules.length - getRulesCountForAction('deactivate') }} rule{{ sortedAndFilteredRules.length - getRulesCountForAction('deactivate') > 1 ? 's are' : ' is' }} already inactive) |
| 179 | + </span> |
| 180 | + </template> |
| 181 | + <template v-else> |
| 182 | + All {{ sortedAndFilteredRules.length }} filtered rule{{ sortedAndFilteredRules.length > 1 ? 's are' : ' is' }} already <strong>inactive</strong>. |
| 183 | + </template> |
168 | 184 | </span> |
169 | 185 | <span v-else-if="bulkActionModal.action === 'delete'"> |
170 | | - Are you sure you want to <strong>delete</strong> all {{ sortedAndFilteredRules.length }} filtered rules? |
| 186 | + Are you sure you want to <strong>delete</strong> all {{ sortedAndFilteredRules.length }} filtered rule{{ sortedAndFilteredRules.length > 1 ? 's' : '' }}? |
171 | 187 | <br><strong class="text-red-600">This action is irreversible.</strong> |
172 | 188 | </span> |
173 | 189 | </p> |
|
180 | 196 | Cancel |
181 | 197 | </a> |
182 | 198 | <a |
183 | | - @click="executeBulkAction" |
184 | | - class="px-4 py-2 text-white rounded-lg transition duration-150 cursor-pointer" |
| 199 | + @click="(bulkActionModal.action === 'activate' && getRulesCountForAction('activate') === 0) || (bulkActionModal.action === 'deactivate' && getRulesCountForAction('deactivate') === 0) ? null : executeBulkAction" |
| 200 | + class="px-4 py-2 text-white rounded-lg transition duration-150" |
185 | 201 | :class="{ |
186 | | - 'bg-green-600 hover:bg-green-700': bulkActionModal.action === 'activate', |
187 | | - 'bg-yellow-600 hover:bg-yellow-700': bulkActionModal.action === 'deactivate', |
188 | | - 'bg-red-600 hover:bg-red-700': bulkActionModal.action === 'delete' |
| 202 | + 'bg-green-600 hover:bg-green-700 cursor-pointer': bulkActionModal.action === 'activate' && getRulesCountForAction('activate') > 0, |
| 203 | + 'bg-yellow-600 hover:bg-yellow-700 cursor-pointer': bulkActionModal.action === 'deactivate' && getRulesCountForAction('deactivate') > 0, |
| 204 | + 'bg-red-600 hover:bg-red-700 cursor-pointer': bulkActionModal.action === 'delete', |
| 205 | + 'bg-gray-400 cursor-not-allowed': (bulkActionModal.action === 'activate' && getRulesCountForAction('activate') === 0) || (bulkActionModal.action === 'deactivate' && getRulesCountForAction('deactivate') === 0) |
189 | 206 | }" |
190 | 207 | > |
191 | | - <span v-if="bulkActionModal.action === 'activate'">Activate All</span> |
192 | | - <span v-else-if="bulkActionModal.action === 'deactivate'">Deactivate All</span> |
| 208 | + <span v-if="bulkActionModal.action === 'activate'"> |
| 209 | + <template v-if="getRulesCountForAction('activate') > 0">Activate {{ getRulesCountForAction('activate') }} rule{{ getRulesCountForAction('activate') > 1 ? 's' : '' }}</template> |
| 210 | + <template v-else>All Already Active</template> |
| 211 | + </span> |
| 212 | + <span v-else-if="bulkActionModal.action === 'deactivate'"> |
| 213 | + <template v-if="getRulesCountForAction('deactivate') > 0">Deactivate {{ getRulesCountForAction('deactivate') }} rule{{ getRulesCountForAction('deactivate') > 1 ? 's' : '' }}</template> |
| 214 | + <template v-else>All Already Inactive</template> |
| 215 | + </span> |
193 | 216 | <span v-else-if="bulkActionModal.action === 'delete'">Delete All</span> |
194 | 217 | </a> |
195 | 218 | </div> |
@@ -219,6 +242,29 @@ const bulkActionModal = ref({ |
219 | 242 | action: null // 'activate', 'deactivate', 'delete' |
220 | 243 | }); |
221 | 244 |
|
| 245 | +const rulesToActivate = computed(() => { |
| 246 | + return sortedAndFilteredRules.value.filter(rule => !rule.active); |
| 247 | +}); |
| 248 | +
|
| 249 | +const rulesToDeactivate = computed(() => { |
| 250 | + return sortedAndFilteredRules.value.filter(rule => rule.active); |
| 251 | +}); |
| 252 | +
|
| 253 | +const getRulesCountForAction = computed(() => { |
| 254 | + return (action) => { |
| 255 | + switch (action) { |
| 256 | + case 'activate': |
| 257 | + return rulesToActivate.value.length; |
| 258 | + case 'deactivate': |
| 259 | + return rulesToDeactivate.value.length; |
| 260 | + case 'delete': |
| 261 | + return sortedAndFilteredRules.value.length; |
| 262 | + default: |
| 263 | + return 0; |
| 264 | + } |
| 265 | + }; |
| 266 | +}); |
| 267 | +
|
222 | 268 | const handleStatusUpdate = ({ ruleId, newStatus }) => { |
223 | 269 | if (sortKey.value === 'active') { |
224 | 270 | const currentSortKey = sortKey.value; |
@@ -251,10 +297,22 @@ const handleDeleteRule = async (ruleId) => { |
251 | 297 | }); |
252 | 298 | } |
253 | 299 | } else { |
254 | | - const errorData = await response.json().catch(() => ({})); |
| 300 | + let errorMessage = 'Unknown error'; |
| 301 | + try { |
| 302 | + const contentType = response.headers.get('content-type'); |
| 303 | + if (contentType && contentType.includes('application/json')) { |
| 304 | + const errorData = await response.json(); |
| 305 | + errorMessage = errorData.message || errorData.error || 'Unknown error'; |
| 306 | + } else { |
| 307 | + errorMessage = `HTTP ${response.status}: ${response.statusText}`; |
| 308 | + } |
| 309 | + } catch (parseError) { |
| 310 | + errorMessage = `HTTP ${response.status}: ${response.statusText}`; |
| 311 | + } |
| 312 | + |
255 | 313 | emit('show-notification', { |
256 | 314 | type: 'error', |
257 | | - message: `Error deleting rule: ${errorData.message || 'Unknown error'}` |
| 315 | + message: `Error deleting rule: ${errorMessage}` |
258 | 316 | }); |
259 | 317 | } |
260 | 318 | } catch (error) { |
@@ -398,13 +456,37 @@ fetch(`${BASE_URL}/rules/sigma/list`, { |
398 | 456 | 'authorization': `Bearer ${localStorage.getItem('auth_token')}`, |
399 | 457 | }, |
400 | 458 | }) |
401 | | -.then(response => response.json()) |
| 459 | +.then(response => { |
| 460 | + if (!response.ok) { |
| 461 | + return response.text().then(text => { |
| 462 | + console.error(`HTTP ${response.status} error response:`, text.substring(0, 500)); |
| 463 | + throw new Error(`HTTP ${response.status}: ${response.statusText}. Response: ${text.substring(0, 200)}`); |
| 464 | + }); |
| 465 | + } |
| 466 | + |
| 467 | + const contentType = response.headers.get('content-type'); |
| 468 | + |
| 469 | + if (!contentType || !contentType.includes('application/json')) { |
| 470 | + return response.text().then(text => { |
| 471 | + console.error('Expected JSON but received:', text.substring(0, 200) + (text.length > 200 ? '...' : '')); |
| 472 | + throw new Error(`Expected JSON response but got: ${contentType}. Response: ${text.substring(0, 100)}`); |
| 473 | + }); |
| 474 | + } |
| 475 | + |
| 476 | + return response.json(); |
| 477 | +}) |
402 | 478 | .then(data => { |
403 | 479 | rules.value = data; |
404 | 480 | isLoading.value = false; |
405 | 481 | }) |
406 | 482 | .catch(error => { |
407 | 483 | console.error('Error fetching rules:', error); |
| 484 | + |
| 485 | + emit('show-notification', { |
| 486 | + message: `Failed to load rules: ${error.message}`, |
| 487 | + type: 'error' |
| 488 | + }); |
| 489 | + |
408 | 490 | isLoading.value = false; |
409 | 491 | }); |
410 | 492 |
|
@@ -432,13 +514,38 @@ const closeBulkActionModal = () => { |
432 | 514 |
|
433 | 515 | const executeBulkAction = async () => { |
434 | 516 | const action = bulkActionModal.value.action; |
435 | | - const rulesToProcess = sortedAndFilteredRules.value.slice(); |
| 517 | + let rulesToProcess = sortedAndFilteredRules.value.slice(); |
| 518 | + |
| 519 | + if (action === 'activate') { |
| 520 | + rulesToProcess = rulesToProcess.filter(rule => !rule.active); |
| 521 | + } else if (action === 'deactivate') { |
| 522 | + rulesToProcess = rulesToProcess.filter(rule => rule.active); |
| 523 | + } |
436 | 524 | |
437 | 525 | closeBulkActionModal(); |
| 526 | + |
| 527 | + if (rulesToProcess.length === 0) { |
| 528 | + let message = ''; |
| 529 | + if (action === 'activate') { |
| 530 | + message = 'All filtered rules are already active'; |
| 531 | + } else if (action === 'deactivate') { |
| 532 | + message = 'All filtered rules are already inactive'; |
| 533 | + } |
| 534 | + |
| 535 | + if (message) { |
| 536 | + emit('show-notification', { |
| 537 | + type: 'info', |
| 538 | + message: message |
| 539 | + }); |
| 540 | + } |
| 541 | + return; |
| 542 | + } |
| 543 | + |
438 | 544 | isBulkActionRunning.value = true; |
439 | 545 | |
440 | 546 | let successCount = 0; |
441 | 547 | let errorCount = 0; |
| 548 | + let skippedCount = sortedAndFilteredRules.value.length - rulesToProcess.length; |
442 | 549 | |
443 | 550 | try { |
444 | 551 | for (const rule of rulesToProcess) { |
@@ -494,8 +601,14 @@ const executeBulkAction = async () => { |
494 | 601 | message = `${successCount} rule${successCount > 1 ? 's' : ''} deleted successfully`; |
495 | 602 | } else if (action === 'activate') { |
496 | 603 | message = `${successCount} rule${successCount > 1 ? 's' : ''} activated successfully`; |
| 604 | + if (skippedCount > 0) { |
| 605 | + message += ` (${skippedCount} already active)`; |
| 606 | + } |
497 | 607 | } else if (action === 'deactivate') { |
498 | 608 | message = `${successCount} rule${successCount > 1 ? 's' : ''} deactivated successfully`; |
| 609 | + if (skippedCount > 0) { |
| 610 | + message += ` (${skippedCount} already inactive)`; |
| 611 | + } |
499 | 612 | } |
500 | 613 | |
501 | 614 | emit('show-notification', { |
|
0 commit comments