main
vue 123 lines 3.52 KB
Raw
1 <template>
2 <div>
3 <CardEntity embedded clickable hoverable size="small" :loading="loadingDetails" @click="showDetails = true">
4 <template #headerMain>{{ id }}</template>
5 <template #headerExtra>
6 <span v-if="groupDetails" class="text-default">
7 {{ groupDetails.external_id }}
8 </span>
9 <n-skeleton v-else text :width="100" :height="18" />
10 </template>
11 <template #default>
12 <div v-if="groupDetails">
13 {{ groupDetails.name }}
14 </div>
15 <n-skeleton v-else text style="width: 60%" :height="20" />
16 </template>
17 <template #footer>
18 <p v-if="groupDetails" class="cursor-text" @click.stop="() => {}">
19 <Markdown :source="groupDetails.description" />
20 </p>
21 <div v-else>
22 <n-skeleton text :repeat="2" :height="16" />
23 <n-skeleton text style="width: 40%" :height="16" />
24 </div>
25 </template>
26 </CardEntity>
27 <n-modal
28 v-model:show="showDetails"
29 display-directive="show"
30 preset="card"
31 content-class="p-0!"
32 :style="{ maxWidth: 'min(900px, 90vw)', minHeight: 'min(600px, 90vh)', overflow: 'hidden' }"
33 :title="`Group • ${id}`"
34 :bordered="false"
35 segmented
36 >
37 <n-tabs type="line" animated :tabs-padding="24">
38 <n-tab-pane name="Overview" tab="Overview" display-directive="show:lazy">
39 <div class="px-6 pt-3 pb-6">
40 <GroupDetails :entity="groupDetails" />
41 </div>
42 </n-tab-pane>
43 <n-tab-pane
44 name="Software"
45 :tab="`Software (${groupDetails?.software?.length || 0})`"
46 display-directive="show:lazy"
47 >
48 <div class="px-6 pt-3 pb-6">
49 <SoftwareList v-if="groupDetails" :list="groupDetails.software" />
50 </div>
51 </n-tab-pane>
52 <n-tab-pane
53 name="Techniques"
54 :tab="`Techniques (${groupDetails?.techniques?.length || 0})`"
55 display-directive="show:lazy"
56 >
57 <div class="px-6 pt-3 pb-6">
58 <TechniquesList v-if="groupDetails" :list="groupDetails.techniques" />
59 </div>
60 </n-tab-pane>
61 </n-tabs>
62 </n-modal>
63 </div>
64 </template>
65
66 <script setup lang="ts">
67 // TODO-FE: refactor
68 import type { MitreGroupDetails } from "@/types/mitre.d"
69 import { NModal, NSkeleton, NTabPane, NTabs, useMessage } from "naive-ui"
70 import { defineAsyncComponent, onBeforeMount, ref } from "vue"
71 import Api from "@/api"
72 import CardEntity from "@/components/common/cards/CardEntity.vue"
73 import SoftwareList from "../Software/SoftwareList.vue"
74 import TechniquesList from "../Technique/TechniquesList.vue"
75 import GroupDetails from "./GroupDetails.vue"
76
77 const { id, entity } = defineProps<{
78 id: string
79 entity?: MitreGroupDetails
80 }>()
81
82 const emit = defineEmits<{
83 (e: "loaded", value: MitreGroupDetails): void
84 }>()
85
86 const Markdown = defineAsyncComponent(() => import("@/components/common/Markdown.vue"))
87
88 const showDetails = ref(false)
89 const message = useMessage()
90 const loadingDetails = ref(false)
91 const groupDetails = ref<MitreGroupDetails | undefined>(undefined)
92
93 function getDetails(id: string) {
94 loadingDetails.value = true
95
96 Api.wazuh.mitre
97 .getMitreGroups({ id })
98 .then(res => {
99 if (res.data.success) {
100 if (res.data.results?.[0]) {
101 groupDetails.value = res.data.results[0]
102 }
103 if (groupDetails.value) emit("loaded", groupDetails.value)
104 } else {
105 message.warning(res.data?.message || "An error occurred. Please try again later.")
106 }
107 })
108 .catch(err => {
109 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
110 })
111 .finally(() => {
112 loadingDetails.value = false
113 })
114 }
115
116 onBeforeMount(() => {
117 if (entity) {
118 groupDetails.value = entity
119 } else if (id) {
120 getDetails(id)
121 }
122 })
123 </script>