main
vue 106 lines 2.96 KB
Raw
1 <template>
2 <div class="flex flex-wrap items-center justify-between gap-3">
3 <div class="flex items-center gap-2 text-sm">
4 <span>Customer</span>
5 <n-select
6 v-model:value="customer"
7 size="small"
8 placeholder="Select a customer"
9 :options="customerOptions"
10 :show-checkmark="false"
11 class="min-w-52"
12 :loading="customerBootstrapLoading"
13 />
14 </div>
15 <div class="flex items-center gap-2">
16 <n-button size="small" :disabled="!customer" @click="showConsolidation = true">
17 <template #icon>
18 <Icon :name="ConsolidateIcon" />
19 </template>
20 Consolidate lessons
21 </n-button>
22 <n-button size="small" :disabled="!customer" :loading @click="refresh()">
23 <template #icon>
24 <Icon :name="RefreshIcon" />
25 </template>
26 Refresh
27 </n-button>
28 </div>
29
30 <n-drawer
31 v-model:show="showConsolidation"
32 :width="640"
33 placement="right"
34 display-directive="show"
35 class="max-w-[98vw]"
36 >
37 <n-drawer-content closable :native-scrollbar="false">
38 <template #header>
39 Palace consolidation
40 <code class="ml-2">{{ customer }}</code>
41 </template>
42
43 <PalaceConsolidation v-if="customer" :customer-code="customer" />
44 </n-drawer-content>
45 </n-drawer>
46 </div>
47 </template>
48
49 <script setup lang="ts">
50 import type { ApiError } from "@/types/common"
51 import { NButton, NDrawer, NDrawerContent, NSelect, useMessage } from "naive-ui"
52 import { onBeforeMount, ref } from "vue"
53 import Api from "@/api"
54 import Icon from "@/components/common/Icon.vue"
55 import { getApiErrorMessage } from "@/utils"
56 import PalaceConsolidation from "./PalaceConsolidation.vue"
57
58 const emit = defineEmits<{
59 (e: "refresh"): void
60 }>()
61 const RefreshIcon = "carbon:renew"
62 const ConsolidateIcon = "carbon:data-collection"
63
64 const message = useMessage()
65
66 const loading = defineModel<boolean>("loading")
67
68 const customer = defineModel<string | null>("customer")
69 const customerOptions = ref<{ label: string; value: string }[]>([])
70 const customerBootstrapLoading = ref(false)
71
72 const showConsolidation = ref(false)
73
74 async function bootstrapCustomers() {
75 // Bootstrap customer picker off alerts_with_reports so we only list
76 // customers that actually have AI runs — no external customer endpoint call.
77 customerBootstrapLoading.value = true
78
79 try {
80 const res = await Api.aiAnalyst.getAlertsWithReports()
81 if (res.data.success) {
82 const codes = new Set((res.data.alerts || []).map(a => a.customer_code))
83 customerOptions.value = Array.from(codes)
84 .sort()
85 .map(c => ({ label: c, value: c }))
86 if (customerOptions.value.length && !customer.value) {
87 // The watch(customer, loadStats) below picks this up — no
88 // need to call loadStats() explicitly from bootstrap.
89 customer.value = customerOptions.value[0].value
90 }
91 }
92 } catch (err) {
93 message.error(getApiErrorMessage(err as ApiError) || "Failed to load customers")
94 } finally {
95 customerBootstrapLoading.value = false
96 }
97 }
98
99 function refresh() {
100 emit("refresh")
101 }
102
103 onBeforeMount(() => {
104 bootstrapCustomers()
105 })
106 </script>