main
vue 138 lines 3.92 KB
Raw
1 <template>
2 <n-card
3 class="artifact-card"
4 :class="{ 'cursor-pointer': hoverable, clickable }"
5 content-style="padding: 16px"
6 @click="emit('click', artifact)"
7 >
8 <div class="flex flex-col gap-3">
9 <div class="flex items-start justify-between gap-4">
10 <div class="flex grow flex-col gap-1">
11 <div class="flex items-center gap-2">
12 <Icon :name="FileIcon" :size="18" class="text-primary-color" />
13 <span class="text-primary-color font-bold">{{ artifact.artifact_name }}</span>
14 </div>
15 <div class="text-secondary-color flex items-center gap-2 text-sm">
16 <span class="font-mono">{{ artifact.file_name }}</span>
17 </div>
18 </div>
19 <n-badge :value="artifact.status" :type="statusType" />
20 </div>
21
22 <n-divider class="my-1!" />
23
24 <div class="flex flex-col gap-2 text-sm">
25 <div class="flex items-center justify-between">
26 <span class="text-secondary-color">Flow ID:</span>
27 <code class="font-mono text-xs">{{ artifact.flow_id }}</code>
28 </div>
29 <div class="flex items-center justify-between">
30 <span class="text-secondary-color">File Size:</span>
31 <span>{{ fileSize }}</span>
32 </div>
33 <div class="flex items-center justify-between">
34 <span class="text-secondary-color">Collected:</span>
35 <span>{{ formatDate(artifact.collection_time, dFormats.datetime) }}</span>
36 </div>
37 <div v-if="artifact.customer_code" class="flex items-center justify-between">
38 <span class="text-secondary-color">Customer:</span>
39 <span>{{ artifact.customer_code }}</span>
40 </div>
41 </div>
42
43 <div v-if="showActions" class="flex gap-2">
44 <n-button size="small" secondary type="info" @click.stop="emit('details', artifact)">
45 <template #icon>
46 <Icon :name="InfoIcon" />
47 </template>
48 Details
49 </n-button>
50 <n-button size="small" secondary type="primary" @click.stop="emit('download', artifact)">
51 <template #icon>
52 <Icon :name="DownloadIcon" />
53 </template>
54 Download
55 </n-button>
56 <n-button size="small" secondary type="error" @click.stop="emit('delete', artifact)">
57 <template #icon>
58 <Icon :name="DeleteIcon" />
59 </template>
60 Delete
61 </n-button>
62 </div>
63 </div>
64 </n-card>
65 </template>
66
67 <script setup lang="ts">
68 import type { BadgeProps } from "naive-ui"
69 import type { AgentArtifactData } from "@/types/agents.d"
70 import bytes from "bytes"
71 import { NBadge, NButton, NCard, NDivider } from "naive-ui"
72 import { computed } from "vue"
73 import Icon from "@/components/common/Icon.vue"
74 import { useSettingsStore } from "@/stores/settings"
75 import { formatDate } from "@/utils/format"
76
77 // TODO-FE: join ArtifactCard + ArtifactCardCompact
78
79 const {
80 artifact,
81 showActions = false,
82 hoverable = false,
83 clickable = false
84 } = defineProps<{
85 artifact: AgentArtifactData
86 showActions?: boolean
87 hoverable?: boolean
88 clickable?: boolean
89 }>()
90
91 const emit = defineEmits<{
92 (e: "click", artifact: AgentArtifactData): void
93 (e: "download", artifact: AgentArtifactData): void
94 (e: "delete", artifact: AgentArtifactData): void
95 (e: "details", artifact: AgentArtifactData): void
96 }>()
97
98 const dFormats = useSettingsStore().dateFormat
99
100 const FileIcon = "lsicon:file-zip-outline"
101 const DownloadIcon = "carbon:download"
102 const DeleteIcon = "carbon:trash-can"
103 const InfoIcon = "carbon:information"
104
105 const STATUS_TYPE_MAP: Record<string, BadgeProps["type"]> = {
106 completed: "success",
107 failed: "error",
108 processing: "warning",
109 pending: "info"
110 } as const
111
112 const fileSize = computed(() => bytes(artifact.file_size))
113
114 const statusType = computed<BadgeProps["type"]>(() => {
115 const normalizedStatus = artifact.status.toLowerCase()
116 return STATUS_TYPE_MAP[normalizedStatus] ?? "default"
117 })
118 </script>
119
120 <style lang="scss" scoped>
121 // TODO-FE: remove style
122
123 .artifact-card {
124 border-radius: var(--border-radius);
125 overflow: hidden;
126 transition: all 0.2s var(--bezier-ease);
127
128 &.hoverable {
129 &:hover {
130 box-shadow: 0 0 0 1px var(--primary-color);
131 }
132 }
133
134 &.clickable {
135 cursor: pointer;
136 }
137 }
138 </style>