main
vue 243 lines 6.42 KB
Raw
1 <template>
2 <div class="customer-integration-form flex min-h-120 flex-col gap-6 overflow-hidden">
3 <div>
4 <n-scrollbar x-scrollable trigger="none">
5 <div class="p-1 pr-4">
6 <n-steps :current size="small" :status="currentStatus">
7 <n-step title="Choose Integration" />
8 <n-step title="Set Auth Keys">
9 <template #icon>
10 <Icon v-if="!isAuthKeysStepEnabled" :name="SkipIcon" />
11 </template>
12 </n-step>
13 </n-steps>
14 </div>
15 </n-scrollbar>
16 </div>
17
18 <div class="flex grow flex-col gap-2 overflow-hidden">
19 <Transition :name="`slide-form-${slideFormDirection}`">
20 <div v-if="current === 1" class="available-list grow overflow-hidden">
21 <n-scrollbar class="max-h-100" trigger="none">
22 <IntegrationsList
23 v-model:selected="selectedIntegration"
24 embedded
25 hide-totals
26 selectable
27 :disabled-ids-list
28 class="pr-4"
29 />
30 </n-scrollbar>
31 </div>
32 <div v-else class="auth-key-form flex flex-wrap gap-3">
33 <template v-for="ak of authKeysForm" :key="ak.key">
34 <n-form-item v-if="ak.type === 'string'" :label="ak.key" required class="grow">
35 <n-input v-model:value="ak.value" :placeholder="`Input ${ak.key}...`" clearable />
36 </n-form-item>
37 <n-form-item v-if="ak.type === 'selectType'" :label="ak.key" required class="grow">
38 <n-select
39 v-model:value="ak.value"
40 :options="apiTypeOptions"
41 :placeholder="`Input ${ak.key}...`"
42 class="min-w-36"
43 clearable
44 />
45 </n-form-item>
46 </template>
47 </div>
48 </Transition>
49 </div>
50
51 <div class="flex justify-between gap-4">
52 <div class="flex gap-4">
53 <n-button @click="close()">Close</n-button>
54 </div>
55 <div class="flex gap-4">
56 <n-button v-if="isPrevStepEnabled" @click="prev()">
57 <template #icon>
58 <Icon :name="ArrowLeftIcon" />
59 </template>
60 Prev
61 </n-button>
62 <n-button v-if="isNextStepShown" :disabled="!isNextStepEnabled" icon-placement="right" @click="next()">
63 <template #icon>
64 <Icon :name="ArrowRightIcon" />
65 </template>
66 Next
67 </n-button>
68 <n-button v-if="isSubmitEnabled" type="primary" :disabled="!isSubmitValid" :loading @click="submit()">
69 Submit
70 </n-button>
71 </div>
72 </div>
73 </div>
74 </template>
75
76 <script setup lang="ts">
77 // TODO-FE: refactor
78 import type { StepsProps } from "naive-ui"
79 import type { NewIntegration } from "@/api/endpoints/integrations"
80 import type { ServiceItemData } from "@/components/services/types"
81 import { NButton, NFormItem, NInput, NScrollbar, NSelect, NStep, NSteps, useMessage } from "naive-ui"
82 import { computed, ref, watch } from "vue"
83 import Api from "@/api"
84 import Icon from "@/components/common/Icon.vue"
85 import IntegrationsList from "@/components/integrations/IntegrationsList.vue"
86
87 interface AuthKeysInput {
88 key: string
89 value: string
90 type: "selectType" | "string"
91 }
92
93 const { customerCode, customerName, disabledIdsList } = defineProps<{
94 customerCode: string
95 customerName: string
96 disabledIdsList?: (string | number)[]
97 }>()
98
99 const emit = defineEmits<{
100 (e: "update:loading", value: boolean): void
101 (e: "close"): void
102 (e: "submitted"): void
103 }>()
104
105 const SkipIcon = "carbon:subtract"
106 const ArrowRightIcon = "carbon:arrow-right"
107 const ArrowLeftIcon = "carbon:arrow-left"
108
109 const message = useMessage()
110 const current = ref<number>(1)
111 const currentStatus = ref<StepsProps["status"]>("process")
112 const slideFormDirection = ref<"right" | "left">("right")
113
114 const selectedIntegration = ref<ServiceItemData | null>(null)
115 const authKeysForm = ref<AuthKeysInput[]>([])
116 const apiTypeOptions = [
117 { label: "Commercial", value: "commercial" },
118 { label: "GCC", value: "gcc" },
119 { label: "GCC-High", value: "gcc-high" }
120 ]
121
122 watch(selectedIntegration, val => {
123 authKeysForm.value = []
124
125 if (val !== null) {
126 for (const ak of val.keys) {
127 authKeysForm.value.push({
128 key: ak.auth_key_name,
129 value: ak.auth_key_name === "API_TYPE" ? (apiTypeOptions[0]?.value ?? "") : "",
130 type: ak.auth_key_name === "API_TYPE" ? "selectType" : "string"
131 })
132 }
133 }
134 })
135
136 const isAuthKeysStepEnabled = computed(() => selectedIntegration.value !== null)
137 const isNextStepShown = computed(() => current.value === 1)
138 const isNextStepEnabled = computed(() => isNextStepShown.value && isAuthKeysStepEnabled.value)
139 const isPrevStepEnabled = computed(() => current.value > 1)
140 const isSubmitEnabled = computed(() => current.value === 2)
141 const isSubmitValid = computed(() => {
142 if (!isSubmitEnabled.value) {
143 return false
144 }
145
146 const keys = authKeysForm.value.length
147 const valid = authKeysForm.value.filter(o => !!o.value).length
148
149 return valid === keys
150 })
151
152 const loading = ref(false)
153
154 function submit() {
155 if (selectedIntegration.value) {
156 currentStatus.value = "finish"
157 loading.value = true
158
159 const payload: NewIntegration = {
160 customer_code: customerCode,
161 customer_name: customerName,
162 integration_name: selectedIntegration.value.name,
163 integration_auth_keys: authKeysForm.value.map(o => ({
164 auth_key_name: o.key,
165 auth_value: o.value
166 }))
167 }
168
169 Api.integrations
170 .createIntegration(payload)
171 .then(res => {
172 if (res.data.success) {
173 emit("submitted")
174 reset()
175 message.success(res.data?.message || "Customer integration successfully created.")
176 } else {
177 message.warning(res.data?.message || "An error occurred. Please try again later.")
178 }
179 })
180 .catch(err => {
181 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
182 })
183 .finally(() => {
184 loading.value = false
185 })
186 }
187 }
188
189 function close() {
190 reset()
191 emit("close")
192 }
193
194 function reset() {
195 currentStatus.value = "process"
196 slideFormDirection.value = "right"
197 current.value = 1
198
199 selectedIntegration.value = null
200 authKeysForm.value = []
201 }
202
203 function next() {
204 currentStatus.value = "process"
205 slideFormDirection.value = "right"
206 current.value++
207 }
208
209 function prev() {
210 currentStatus.value = "process"
211 slideFormDirection.value = "left"
212 current.value--
213 }
214 </script>
215
216 <style lang="scss" scoped>
217 .customer-integration-form {
218 .slide-form-right-enter-active,
219 .slide-form-right-leave-active,
220 .slide-form-left-enter-active,
221 .slide-form-left-leave-active {
222 transition: all 0.2s ease-out;
223 position: absolute;
224 width: 100%;
225 }
226
227 .slide-form-left-enter-from {
228 transform: translateX(-100%);
229 }
230
231 .slide-form-left-leave-to {
232 transform: translateX(100%);
233 }
234
235 .slide-form-right-enter-from {
236 transform: translateX(100%);
237 }
238
239 .slide-form-right-leave-to {
240 transform: translateX(-100%);
241 }
242 }
243 </style>