| 1 | <template> |
| 2 | <n-popover v-model:show="show" trigger="manual" to="body" content-class="px-0" @clickoutside="closePopup()"> |
| 3 | <template #trigger> |
| 4 | <slot :loading :toggle-popup /> |
| 5 | </template> |
| 6 | |
| 7 | <div class="flex flex-col gap-4 py-1"> |
| 8 | <div class="flex items-center gap-2"> |
| 9 | <n-input-group> |
| 10 | <n-select |
| 11 | v-model:value="model.unit" |
| 12 | :options="unitOptions" |
| 13 | placeholder="Time unit" |
| 14 | :disabled="loading" |
| 15 | class="w-28!" |
| 16 | /> |
| 17 | <n-input-number |
| 18 | v-model:value="model.time" |
| 19 | :min="1" |
| 20 | clearable |
| 21 | placeholder="Time" |
| 22 | class="w-32!" |
| 23 | :disabled="loading" |
| 24 | /> |
| 25 | </n-input-group> |
| 26 | </div> |
| 27 | |
| 28 | <div class="flex justify-between gap-2"> |
| 29 | <n-button quaternary size="small" @click="closePopup()">Close</n-button> |
| 30 | <n-button |
| 31 | :disabled="!dirty || !isValid" |
| 32 | :loading |
| 33 | type="primary" |
| 34 | size="small" |
| 35 | @click="updateTimeInterval()" |
| 36 | > |
| 37 | Save |
| 38 | </n-button> |
| 39 | </div> |
| 40 | </div> |
| 41 | </n-popover> |
| 42 | </template> |
| 43 | |
| 44 | <script setup lang="ts"> |
| 45 | // TODO-FE: refactor |
| 46 | import type { SigmaQuery, SigmaTimeInterval, SigmaTimeIntervalUnit } from "@/types/sigma.d" |
| 47 | import { NButton, NInputGroup, NInputNumber, NPopover, NSelect, useMessage } from "naive-ui" |
| 48 | import { computed, onBeforeMount, ref, toRefs, watch } from "vue" |
| 49 | import Api from "@/api" |
| 50 | |
| 51 | const props = defineProps<{ |
| 52 | query: SigmaQuery |
| 53 | }>() |
| 54 | |
| 55 | const emit = defineEmits<{ |
| 56 | (e: "updated", value: SigmaQuery): void |
| 57 | }>() |
| 58 | |
| 59 | const { query } = toRefs(props) |
| 60 | |
| 61 | const loading = defineModel<boolean | undefined>("loading", { default: false }) |
| 62 | |
| 63 | const show = ref(false) |
| 64 | const lastShow = ref(Date.now()) |
| 65 | const message = useMessage() |
| 66 | const model = ref<{ time: number; unit: SigmaTimeIntervalUnit }>({ time: 1, unit: "m" }) |
| 67 | const unitOptions = [ |
| 68 | { label: "Minutes", value: "m" }, |
| 69 | { label: "Hours", value: "h" }, |
| 70 | { label: "Days", value: "d" } |
| 71 | ] |
| 72 | const timeUnit = ref<SigmaTimeIntervalUnit>("m") |
| 73 | const timeValue = ref<number>(1) |
| 74 | |
| 75 | const timeInterval = computed<SigmaTimeInterval>(() => `${timeValue.value}${timeUnit.value}`) |
| 76 | const modelTimeInterval = computed<SigmaTimeInterval>(() => `${model.value.time}${model.value.unit}`) |
| 77 | |
| 78 | const dirty = computed(() => timeInterval.value !== modelTimeInterval.value) |
| 79 | const isValid = computed(() => model.value.time && model.value.unit) |
| 80 | |
| 81 | watch(show, val => { |
| 82 | if (val && !loading.value) { |
| 83 | setModel() |
| 84 | } |
| 85 | }) |
| 86 | |
| 87 | function togglePopup() { |
| 88 | if (Date.now() - lastShow.value > 500) { |
| 89 | show.value = !show.value |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | function closePopup() { |
| 94 | lastShow.value = Date.now() |
| 95 | show.value = false |
| 96 | } |
| 97 | |
| 98 | const NUMBER_REGEX = /\d+/ |
| 99 | const LETTER_REGEX = /[a-z]/i |
| 100 | |
| 101 | function setModel() { |
| 102 | if (query.value.time_interval) { |
| 103 | timeUnit.value = ( |
| 104 | query.value.time_interval.match(LETTER_REGEX)?.[0] || "m" |
| 105 | ).toLocaleLowerCase() as SigmaTimeIntervalUnit |
| 106 | timeValue.value = Number.parseInt(query.value.time_interval.match(NUMBER_REGEX)?.[0] || "1") |
| 107 | |
| 108 | model.value.unit = timeUnit.value |
| 109 | model.value.time = timeValue.value |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | function updateTimeInterval() { |
| 114 | if (query.value.rule_name && modelTimeInterval.value) { |
| 115 | loading.value = true |
| 116 | |
| 117 | Api.sigma |
| 118 | .setQueryTimeInterval(query.value.rule_name, modelTimeInterval.value) |
| 119 | .then(res => { |
| 120 | if (res.data.success) { |
| 121 | if (res.data.sigma_queries[0]) { |
| 122 | emit("updated", res.data.sigma_queries[0]) |
| 123 | } |
| 124 | message.success(res.data?.message || "Sigma query updated successfully") |
| 125 | } else { |
| 126 | message.warning(res.data?.message || "An error occurred. Please try again later.") |
| 127 | } |
| 128 | }) |
| 129 | .catch(err => { |
| 130 | message.error(err.response?.data?.message || "An error occurred. Please try again later.") |
| 131 | }) |
| 132 | .finally(() => { |
| 133 | loading.value = false |
| 134 | }) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | onBeforeMount(() => { |
| 139 | setModel() |
| 140 | }) |
| 141 | </script> |