main
vue 164 lines 4.13 KB
Raw
1 <template>
2 <n-button quaternary class="w-full! justify-start!" @click="showModal = true">
3 <template #icon>
4 <Icon :name="CustomerIcon" :size="14" />
5 </template>
6 Assign Customer
7 </n-button>
8
9 <n-modal
10 v-model:show="showModal"
11 display-directive="show"
12 preset="card"
13 :style="{ maxWidth: 'min(600px, 90vw)', minHeight: 'min(300px, 60vh)' }"
14 title="Assign Customer Access"
15 :bordered="false"
16 content-class="flex flex-col"
17 segmented
18 >
19 <div class="flex flex-col gap-4">
20 <div>
21 <strong>User:</strong>
22 {{ user?.username }}
23 </div>
24
25 <n-form :model="formModel">
26 <n-form-item label="Select Customers">
27 <n-select
28 v-model:value="formModel.customerCodes"
29 :options="customerOptions"
30 placeholder="Choose customers"
31 multiple
32 :loading="loadingCustomers"
33 />
34 </n-form-item>
35
36 <n-form-item label="Current Access">
37 <div v-if="currentAccess.length > 0" class="flex flex-wrap gap-2">
38 <n-tag v-for="customerCode in currentAccess" :key="customerCode" type="info" size="small">
39 {{ customerCode }}
40 </n-tag>
41 </div>
42 <div v-else class="text-gray-500">No customer access assigned</div>
43 </n-form-item>
44 </n-form>
45
46 <div class="flex justify-end gap-3">
47 <n-button @click="showModal = false">Cancel</n-button>
48 <n-button type="primary" :loading @click="handleAssignCustomers">Assign Customers</n-button>
49 </div>
50 </div>
51 </n-modal>
52 </template>
53
54 <script setup lang="ts">
55 // TODO-FE: refactor
56 import type { Customer } from "@/types/customers.d"
57 import type { User } from "@/types/user.d"
58 import { NButton, NForm, NFormItem, NModal, NSelect, NTag, useMessage } from "naive-ui"
59 import { computed, ref, watch } from "vue"
60 import Api from "@/api"
61 import Icon from "@/components/common/Icon.vue"
62
63 const props = defineProps<{
64 user?: User
65 }>()
66
67 const emit = defineEmits<{
68 success: []
69 }>()
70
71 const CustomerIcon = "carbon:user-certification"
72 const message = useMessage()
73 const showModal = ref(false)
74 const loading = ref(false)
75 const loadingCustomers = ref(false)
76 const customers = ref<Customer[]>([])
77 const currentAccess = ref<string[]>([])
78
79 const formModel = ref({
80 customerCodes: [] as string[]
81 })
82
83 const customerOptions = computed(() =>
84 customers.value.map(customer => ({
85 label: `${customer.customer_name} (${customer.customer_code})`,
86 value: customer.customer_code
87 }))
88 )
89
90 async function loadCustomers() {
91 loadingCustomers.value = true
92 try {
93 const res = await Api.customers.getCustomers()
94 if (res.data.success && res.data.customers) {
95 customers.value = res.data.customers
96 }
97 } catch {
98 message.error("Failed to load customers")
99 } finally {
100 loadingCustomers.value = false
101 }
102 }
103
104 async function loadCurrentAccess() {
105 if (!props.user) return
106
107 try {
108 const res = await Api.auth.getUserCustomerAccess(props.user.id)
109 if (res.data.success) {
110 currentAccess.value = res.data.customer_codes || []
111 formModel.value.customerCodes = [...currentAccess.value]
112 }
113 } catch (error) {
114 console.error("Error loading customer access:", error)
115 message.error("Failed to load current customer access")
116 }
117 }
118
119 function handleAssignCustomers() {
120 if (!props.user) return
121
122 loading.value = true
123
124 Api.auth
125 .assignCustomerAccess(props.user.id, formModel.value.customerCodes)
126 .then(res => {
127 if (res.data.success) {
128 message.success(res.data.message || "Customer access assigned successfully")
129 showModal.value = false
130 emit("success")
131 } else {
132 message.error(res.data.message || "Failed to assign customer access")
133 }
134 })
135 .catch(err => {
136 message.error(err.response?.data?.message || "Failed to assign customer access")
137 })
138 .finally(() => {
139 loading.value = false
140 })
141 }
142
143 watch(showModal, newVal => {
144 if (newVal) {
145 loadCustomers()
146 loadCurrentAccess()
147 }
148 })
149
150 // This instance is reused across table rows, so the bound user can change while
151 // mounted. Reload that user's access if it changes while the modal is open, and
152 // clear stale state otherwise so a previous user's data is never shown. See #899.
153 watch(
154 () => props.user?.id,
155 () => {
156 if (showModal.value) {
157 loadCurrentAccess()
158 } else {
159 currentAccess.value = []
160 formModel.value.customerCodes = []
161 }
162 }
163 )
164 </script>