main
vue 152 lines 3.24 KB
Raw
1 <template>
2 <div class="artifact-details flex flex-col gap-4">
3 <n-descriptions :column="1" bordered size="small">
4 <n-descriptions-item v-for="field in visibleFields" :key="field.key" :label="field.label">
5 <component :is="field.component" v-bind="field.props">
6 {{ field.value }}
7 </component>
8 </n-descriptions-item>
9 </n-descriptions>
10 </div>
11 </template>
12
13 <script setup lang="ts">
14 import type { BadgeProps } from "naive-ui"
15 import type { AgentArtifactData } from "@/types/agents.d"
16 import bytes from "bytes"
17 import { NBadge, NDescriptions, NDescriptionsItem } from "naive-ui"
18 import { computed } from "vue"
19 import { useSettingsStore } from "@/stores/settings"
20 import { formatDate } from "@/utils/format"
21
22 const { artifact } = defineProps<{ artifact: AgentArtifactData }>()
23
24 const dFormats = useSettingsStore().dateFormat
25
26 const STATUS_TYPE_MAP: Record<string, BadgeProps["type"]> = {
27 completed: "success",
28 failed: "error",
29 processing: "warning",
30 pending: "info"
31 } as const
32
33 const fileSize = computed(() => bytes(artifact.file_size))
34
35 const statusType = computed<BadgeProps["type"]>(() => {
36 const normalizedStatus = artifact.status.toLowerCase()
37 return STATUS_TYPE_MAP[normalizedStatus] ?? "default"
38 })
39
40 const fields = computed(() => [
41 {
42 key: "id",
43 label: "ID",
44 value: artifact.id,
45 component: "code"
46 },
47 {
48 key: "agent_id",
49 label: "Agent ID",
50 value: artifact.agent_id,
51 component: "code"
52 },
53 {
54 key: "velociraptor_id",
55 label: "Velociraptor ID",
56 value: artifact.velociraptor_id,
57 component: "code"
58 },
59 {
60 key: "customer_code",
61 label: "Customer Code",
62 value: artifact.customer_code || "",
63 condition: !!artifact.customer_code
64 },
65 {
66 key: "artifact_name",
67 label: "Artifact Name",
68 value: artifact.artifact_name
69 },
70 {
71 key: "flow_id",
72 label: "Flow ID",
73 value: artifact.flow_id,
74 component: "code"
75 },
76 {
77 key: "file_name",
78 label: "File Name",
79 value: artifact.file_name
80 },
81 {
82 key: "file_size",
83 label: "File Size",
84 value: fileSize.value
85 },
86 {
87 key: "content_type",
88 label: "Content Type",
89 value: artifact.content_type
90 },
91 {
92 key: "file_hash",
93 label: "File Hash",
94 value: artifact.file_hash,
95 component: "code",
96 props: { class: "text-xs" }
97 },
98 {
99 key: "collection_time",
100 label: "Collection Time",
101 value: formatDate(artifact.collection_time, dFormats.datetime)
102 },
103 {
104 key: "status",
105 label: "Status",
106 value: artifact.status,
107 component: NBadge,
108 props: { value: artifact.status, type: statusType.value }
109 },
110 {
111 key: "bucket_name",
112 label: "Bucket",
113 value: artifact.bucket_name
114 },
115 {
116 key: "object_key",
117 label: "Object Key",
118 value: artifact.object_key,
119 component: "code",
120 props: { class: "text-xs" }
121 },
122 {
123 key: "uploaded_by",
124 label: "Uploaded By",
125 value: artifact.uploaded_by || "",
126 condition: !!artifact.uploaded_by
127 },
128 {
129 key: "notes",
130 label: "Notes",
131 value: artifact.notes || "",
132 condition: !!artifact.notes
133 }
134 ])
135
136 const visibleFields = computed(() => fields.value.filter(field => field.condition !== false))
137 </script>
138
139 <style lang="scss" scoped>
140 // TODO-FE: remove style
141 .artifact-details {
142 :deep() {
143 code {
144 font-family: var(--font-family-mono);
145 font-size: 12px;
146 padding: 2px 4px;
147 background-color: var(--bg-secondary-color);
148 border-radius: 3px;
149 }
150 }
151 }
152 </style>