main
vue 141 lines 3.86 KB
Raw
1 <template>
2 <div>
3 <n-button :size="size || 'small'" ghost type="primary" @click="openRecommendations()">
4 <template #icon>
5 <Icon :name="AiIcon" />
6 </template>
7 Recommend Artifact Collection
8 </n-button>
9
10 <n-modal
11 v-model:show="showModal"
12 preset="card"
13 :style="{ maxWidth: 'min(700px, 90vw)', minHeight: 'min(500px, 90vh)' }"
14 title="Recommend Artifact Collection"
15 :bordered="false"
16 display-directive="show"
17 segmented
18 >
19 <div class="mb-4 flex flex-wrap items-center gap-3">
20 <div>Get Recommendations for OS:</div>
21 <n-select
22 v-model:value="selectedOs"
23 size="small"
24 :options="osOptions"
25 class="w-32!"
26 placeholder="Select OS"
27 :disabled="loading"
28 />
29 <n-button
30 v-if="!recommendations.length"
31 size="small"
32 type="primary"
33 :loading
34 :disabled="!selectedOs"
35 @click="getRecommendations()"
36 >
37 <template #icon>
38 <Icon :name="AiIcon" />
39 </template>
40 Submit
41 </n-button>
42 </div>
43 <n-spin :show="loading" class="min-h-48">
44 <div v-if="recommendations.length" class="flex flex-col gap-2">
45 <n-card
46 v-for="recommendation of recommendations"
47 :key="recommendation.name"
48 embedded
49 content-class="flex flex-col gap-2 p-0!"
50 class="item-appear item-appear-bottom item-appear-005 overflow-hidden"
51 size="small"
52 >
53 <strong class="recommendation-name px-4 pt-3 pb-1 font-mono">{{ recommendation.name }}</strong>
54 <n-divider class="m-0!" />
55 <div class="recommendation-description px-4 pt-1">
56 {{ recommendation.description }}
57 </div>
58 <p class="recommendation-explanation px-4 pt-2 pb-3">
59 {{ recommendation.explanation }}
60 </p>
61 </n-card>
62 </div>
63 <n-empty
64 v-if="!loading && !recommendations.length"
65 description="Recommendations not found"
66 class="h-48 justify-center"
67 />
68 </n-spin>
69 </n-modal>
70 </div>
71 </template>
72
73 <script setup lang="ts">
74 import type { ButtonSize } from "naive-ui"
75 import type { Recommendation } from "@/types/artifacts.d"
76 import type { OsTypesFull } from "@/types/common.d"
77 import _uniqBy from "lodash/uniqBy"
78 import { NButton, NCard, NDivider, NEmpty, NModal, NSelect, NSpin, useMessage } from "naive-ui"
79 import { computed, ref } from "vue"
80 import Api from "@/api"
81 import Icon from "@/components/common/Icon.vue"
82
83 interface RecommendationStore {
84 os: OsTypesFull
85 recommendation: Recommendation[]
86 }
87
88 const { context, size } = defineProps<{
89 context: string | object
90 size?: ButtonSize
91 }>()
92
93 const AiIcon = "mage:stars-c"
94 const showModal = ref<boolean>(false)
95 const loading = ref<boolean>(false)
96 const message = useMessage()
97
98 const recommendationsStore = ref<RecommendationStore[]>([])
99 const selectedOs = ref<OsTypesFull | null>(null)
100 const recommendations = computed<Recommendation[]>(
101 () => recommendationsStore.value.find(item => item.os === selectedOs.value)?.recommendation || []
102 )
103
104 const osOptions: { label: string; value: OsTypesFull }[] = [
105 { label: "Windows", value: "Windows" },
106 { label: "Linux", value: "Linux" },
107 { label: "MacOS", value: "MacOS" }
108 ]
109
110 function openRecommendations() {
111 showModal.value = true
112 }
113
114 function getRecommendations() {
115 if (selectedOs.value !== null && context) {
116 loading.value = true
117
118 const requestedOs = selectedOs.value
119
120 Api.artifacts
121 .getArtifactRecommendation({ os: requestedOs, prompt: context })
122 .then(res => {
123 if (res.data.success) {
124 recommendationsStore.value.push({
125 os: requestedOs,
126 recommendation: res.data?.recommendations || []
127 })
128 recommendationsStore.value = _uniqBy(recommendationsStore.value, "os")
129 } else {
130 message.warning(res.data?.message || "An error occurred. Please try again later.")
131 }
132 })
133 .catch(err => {
134 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
135 })
136 .finally(() => {
137 loading.value = false
138 })
139 }
140 }
141 </script>