@7365admin1/layer-common 1.11.41 → 1.11.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.11.43
4
+
5
+ ### Patch Changes
6
+
7
+ - 72e51d5: update version layer-common
8
+
9
+ ## 1.11.42
10
+
11
+ ### Patch Changes
12
+
13
+ - e11278e: updates and fixes
14
+
3
15
  ## 1.11.41
4
16
 
5
17
  ### Patch Changes
@@ -6,7 +6,8 @@
6
6
 
7
7
  <!-- Loading Skeleton -->
8
8
  <template v-if="loading && files?.length > 0">
9
- <v-card v-for="(file, index) in files" :key="index" height="50px" flat border="sm black" class="mb-2">
9
+ <v-card v-for="(file, index) in files" :key="index" height="50px" flat border="sm black"
10
+ class="mb-2">
10
11
  <v-skeleton-loader type="list-item" class="mb-2" />
11
12
  </v-card>
12
13
  </template>
@@ -56,7 +57,7 @@
56
57
  </template>
57
58
  <v-list-item-title class="text-body-2">{{ item.file.name }}</v-list-item-title>
58
59
  <v-list-item-subtitle class="text-caption">{{ item.mimeType
59
- }}</v-list-item-subtitle>
60
+ }}</v-list-item-subtitle>
60
61
  <template #append>
61
62
  <v-icon size="16" color="grey-lighten-1">mdi-eye-outline</v-icon>
62
63
  </template>
@@ -81,7 +82,7 @@
81
82
  </template>
82
83
  <v-list-item-title class="text-body-2">{{ item.file.name }}</v-list-item-title>
83
84
  <v-list-item-subtitle class="text-caption">{{ item.mimeType
84
- }}</v-list-item-subtitle>
85
+ }}</v-list-item-subtitle>
85
86
  <template #append>
86
87
  <v-icon size="16" color="grey-lighten-1">mdi-open-in-new</v-icon>
87
88
  </template>
@@ -200,26 +201,54 @@ watchEffect(async () => {
200
201
  for (const item of (props.files || [])) {
201
202
  // Handle building files format: { id, name }
202
203
  if (item.id && item.name && !item.file) {
204
+ let file: File = new File([], item.name);
203
205
  let mimeType = 'application/octet-stream';
206
+
204
207
  try {
205
- const fileMeta = await getFileById(item.id) as any;
206
- const apiMimeType = fileMeta?.data?.mimeType || fileMeta?.mimeType;
207
- // Use API mime type only if it's not generic
208
- if (apiMimeType && apiMimeType !== 'application/octet-stream') {
209
- mimeType = apiMimeType;
210
- } else {
211
- // Fallback to filename-based detection
208
+ // Try to get the actual file with proper MIME type detection from blob
209
+ const fileUrl = getFileUrl(item.id);
210
+ file = await urlToFile(fileUrl, item.name);
211
+ mimeType = file.type || 'application/octet-stream';
212
+
213
+ // If blob detection returned generic type, try API metadata or filename
214
+ if (mimeType === 'application/octet-stream') {
215
+ try {
216
+ const fileMeta = await getFileById(item.id) as any;
217
+ const apiMimeType = fileMeta?.data?.mimeType || fileMeta?.mimeType;
218
+ if (apiMimeType && apiMimeType !== 'application/octet-stream') {
219
+ mimeType = apiMimeType;
220
+ } else {
221
+ const ext = item.name?.split('.').pop()?.toLowerCase();
222
+ mimeType = inferMimeType(ext);
223
+ }
224
+ } catch {
225
+ // Fallback to filename-based detection
226
+ const ext = item.name?.split('.').pop()?.toLowerCase();
227
+ mimeType = inferMimeType(ext);
228
+ }
229
+ }
230
+ } catch {
231
+ // Fallback: try API metadata
232
+ try {
233
+ const fileMeta = await getFileById(item.id) as any;
234
+ const apiMimeType = fileMeta?.data?.mimeType || fileMeta?.mimeType;
235
+ if (apiMimeType && apiMimeType !== 'application/octet-stream') {
236
+ mimeType = apiMimeType;
237
+ } else {
238
+ // Fallback to filename-based detection
239
+ const ext = item.name?.split('.').pop()?.toLowerCase();
240
+ mimeType = inferMimeType(ext);
241
+ }
242
+ } catch {
243
+ // Final fallback: infer from filename
212
244
  const ext = item.name?.split('.').pop()?.toLowerCase();
213
245
  mimeType = inferMimeType(ext);
214
246
  }
215
- } catch {
216
- // Fallback: infer MIME type from file name
217
- const ext = item.name?.split('.').pop()?.toLowerCase();
218
- mimeType = inferMimeType(ext);
219
247
  }
248
+
220
249
  processedFiles.push({
221
250
  _id: item.id,
222
- file: new File([], item.name, { type: mimeType }),
251
+ file,
223
252
  preview: item.preview ?? false,
224
253
  mimeType
225
254
  });
@@ -52,12 +52,12 @@
52
52
 
53
53
  <v-col cols="12">
54
54
  <InputLabel class="text-capitalize" title="Start Date" required />
55
- <InputDateTimePicker v-model:utc="bulletinForm.startDate" :rules="[validStartDateRule]" />
55
+ <InputDateTimePicker v-model:utc="bulletinForm.startDate" :rules="[validStartDateRule]"/>
56
56
  </v-col>
57
57
 
58
58
  <v-col cols="12" v-if="!bulletinForm.noExpiration">
59
59
  <InputLabel class="text-capitalize" title="End Date" required />
60
- <InputDateTimePicker v-model:utc="bulletinForm.endDate" :rules="[validExpiryDateRule]" />
60
+ <InputDateTimePicker v-model:utc="bulletinForm.endDate" :rules="[validExpiryDateRule]" :min="today" />
61
61
  </v-col>
62
62
 
63
63
  <v-col cols="12">
@@ -103,6 +103,15 @@
103
103
  </template>
104
104
 
105
105
  <script setup lang="ts">
106
+ const today = computed(() => {
107
+ const d = new Date()
108
+ const yyyy = d.getFullYear()
109
+ const mm = String(d.getMonth() + 1).padStart(2, '0')
110
+ const dd = String(d.getDate()).padStart(2, '0')
111
+ console.log('${yyyy}-${mm}-${dd}',`${yyyy}-${mm}-${dd}`)
112
+ return `${yyyy}-${mm}-${dd}`
113
+ })
114
+
106
115
  const prop = defineProps({
107
116
  siteId: {
108
117
  type: String,
@@ -214,6 +214,7 @@ function eventStatusFormat(status: any) {
214
214
 
215
215
  const tabOptions = [
216
216
  { name: "Active", status: "active" },
217
+ { name: "Upcoming", status: "upcoming" },
217
218
  { name: "Expired", status: "expired" },
218
219
  ];
219
220
 
@@ -332,7 +333,7 @@ watch([searchInput], ([search]) => {
332
333
 
333
334
  onMounted(() => {
334
335
  const statusQuery = (useRoute()?.query?.status)
335
- status.value = (statusQuery === "active" || statusQuery === "expired") ? statusQuery : "active"
336
+ status.value = (statusQuery === "active" || statusQuery === "upcoming" || statusQuery === "expired") ? statusQuery : "active"
336
337
  })
337
338
 
338
339
  </script>
@@ -34,8 +34,7 @@
34
34
 
35
35
  <!-- ACTIONS -->
36
36
  <template #actions>
37
- <v-btn v-if="tab !== 'suspended'" class="text-none" rounded="pill" variant="tonal" size="large"
38
- @click="openInviteDialog">
37
+ <v-btn class="text-none" rounded="pill" variant="tonal" size="large" @click="openInviteDialog">
39
38
  Invite Client
40
39
  </v-btn>
41
40
  </template>
@@ -71,7 +70,7 @@
71
70
  </template>
72
71
 
73
72
  <script setup lang="ts">
74
- import { computed, onMounted, ref } from 'vue'
73
+ import { computed, nextTick, onMounted, ref } from 'vue'
75
74
  import useOrg from '../composables/useOrg'
76
75
  import useVerification from '../composables/useVerification'
77
76
  import useRole from '../composables/useRole'
@@ -82,7 +81,7 @@ import useSubscription from '../composables/useSubscription'
82
81
  const tab = ref<'active' | 'suspended' | 'pending'>('active')
83
82
  const dialog = ref(false)
84
83
 
85
- const { getAll } = useOrg()
84
+ const { getAllV2 } = useOrg()
86
85
  const { getVerifications, cancelUserInvitation } = useVerification()
87
86
  const { getCustomerSites: getCustomerSitesByOrgId } = useCustomerSite()
88
87
  const { getByOrgId: getSubscriptionByOrgId } = useSubscription()
@@ -112,6 +111,7 @@ const APP_LIST = [
112
111
  /* ================= HEADERS ================= */
113
112
  const orgHeaders = [
114
113
  { title: 'Organization Name', key: 'name' },
114
+ { title: 'Email', key: 'email' },
115
115
  { title: 'Sites', key: 'sites' },
116
116
  { title: 'Plan Type', key: 'planType' },
117
117
  { title: 'Billing Cycle', key: 'billingCycle' },
@@ -143,9 +143,22 @@ const pageRange = computed(() => {
143
143
  })
144
144
 
145
145
  /* ================= HELPERS ================= */
146
- function formatDate(date?: string) {
147
- if (!date) return '-'
148
- return new Date(date).toLocaleDateString('en-US')
146
+ function formatDate(date?: string | Date) {
147
+ if (date == null || date === '') return '-'
148
+ const d = typeof date === 'object' && date instanceof Date ? date : new Date(date as string)
149
+ if (Number.isNaN(d.getTime())) return '-'
150
+ return d.toLocaleDateString('en-US')
151
+ }
152
+
153
+ /** Subscription document không luôn có `paidType`; fallback theo type/description. */
154
+ function planTypeLabel(sub: Record<string, any>) {
155
+ if (!sub) return '-'
156
+ if (sub.paidType) return String(sub.paidType)
157
+ if (sub.type === 'organization') return 'Organization'
158
+ if (sub.type === 'affiliate') return 'Affiliate'
159
+ const desc = String(sub.description || '').trim()
160
+ if (desc) return desc.length > 48 ? `${desc.slice(0, 45)}…` : desc
161
+ return '-'
149
162
  }
150
163
 
151
164
  async function resolveRoleName(id: string) {
@@ -159,10 +172,11 @@ async function resolveRoleName(id: string) {
159
172
  const limit = ref(10)
160
173
  /* ================= FETCH ================= */
161
174
  async function fetchActive() {
162
- const res = await getAll({
175
+ const res = await getAllV2({
163
176
  page: page.value,
164
177
  search: search.value,
165
178
  limit: limit.value,
179
+ status: 'active',
166
180
  })
167
181
 
168
182
  const orgs = res?.data?.items || res?.items || []
@@ -189,7 +203,7 @@ async function fetchActive() {
189
203
  const subscription = subRes?.data || subRes
190
204
 
191
205
  if (subscription) {
192
- planType = subscription?.paidType || '-'
206
+ planType = planTypeLabel(subscription)
193
207
  billingCycle = subscription?.billingCycle || '-'
194
208
  subscriptionStart = formatDate(subscription?.createdAt)
195
209
  subscriptionEnd = formatDate(subscription?.nextBillingDate)
@@ -207,6 +221,7 @@ async function fetchActive() {
207
221
  subscriptionStart,
208
222
  subscriptionEnd,
209
223
  status: org?.status || 'active',
224
+ actions: '—',
210
225
  }
211
226
  })
212
227
  )
@@ -225,10 +240,11 @@ async function fetchActive() {
225
240
  }
226
241
 
227
242
  async function fetchSuspended() {
228
- const res = await getAll({
243
+ const res = await getAllV2({
229
244
  page: page.value,
230
245
  search: search.value,
231
- // status: 'suspended',
246
+ limit: limit.value,
247
+ status: 'suspended',
232
248
  })
233
249
  const orgs = res?.data?.items || res?.items || []
234
250
 
@@ -252,7 +268,7 @@ async function fetchSuspended() {
252
268
  const subRes = await getSubscriptionByOrgId(org._id)
253
269
  const subscription = subRes?.data || subRes
254
270
  if (subscription) {
255
- planType = subscription?.paidType || '-'
271
+ planType = planTypeLabel(subscription)
256
272
  billingCycle = subscription?.billingCycle || '-'
257
273
  subscriptionStart = formatDate(subscription?.createdAt)
258
274
  subscriptionEnd = formatDate(subscription?.nextBillingDate)
@@ -262,7 +278,7 @@ async function fetchSuspended() {
262
278
  }
263
279
 
264
280
  return {
265
- _id: org._id,
281
+ _id: `${org._id}-${page.value}`,
266
282
  ...org,
267
283
  sites: `${sitesCount} Sites`,
268
284
  planType,
@@ -270,17 +286,17 @@ async function fetchSuspended() {
270
286
  subscriptionStart,
271
287
  subscriptionEnd,
272
288
  status: org?.status || 'suspended',
289
+ actions: '—',
273
290
  }
274
291
  })
275
292
  )
276
293
 
294
+ items.value = []
295
+ await nextTick()
277
296
  items.value = [...mapped]
278
-
279
- pages.value = res?.pages || 1
280
-
281
- const match = res?.pageRange?.match(/of\s+(\d+)/i)
282
-
283
- totalItems.value = match ? Number(match[1]) : mapped.length
297
+ pages.value = res?.pages || 1
298
+ const match = res?.pageRange?.match(/of\s+(\d+)/i)
299
+ totalItems.value = match ? Number(match[1]) : mapped.length
284
300
  }
285
301
 
286
302
  async function fetchPending() {
@@ -322,9 +338,7 @@ async function fetchData() {
322
338
  await fetchActive()
323
339
  }
324
340
  else if (tab.value === 'suspended') {
325
- items.value = []
326
- pages.value = 1
327
- totalItems.value = 0
341
+ await fetchSuspended()
328
342
  }
329
343
  else {
330
344
  await fetchPending()
@@ -7,7 +7,7 @@
7
7
  </template>
8
8
  </v-text-field>
9
9
  <div class="w-100 d-flex align-end ga-3 hidden-input">
10
- <input ref="dateInput" :type="inputType" v-model="dateTime" />
10
+ <input ref="dateInput" :type="inputType" v-model="dateTime" :min="minValue" />
11
11
  </div>
12
12
  </div>
13
13
  </template>
@@ -27,6 +27,10 @@ const prop = defineProps({
27
27
  dateOnly: {
28
28
  type: Boolean,
29
29
  default: false
30
+ },
31
+ min: {
32
+ type: String,
33
+ default: undefined
30
34
  }
31
35
  })
32
36
 
@@ -36,6 +40,15 @@ const dateTimeUTC = defineModel<string | null>('utc', { default: null }) // UTC
36
40
 
37
41
  const dateTimeFormattedReadOnly = ref<string | null>(null)
38
42
  const inputType = computed(() => (prop.dateOnly ? 'date' : 'datetime-local'))
43
+
44
+
45
+ const minValue = computed(() => {
46
+ if (!prop.min) return undefined
47
+ if (/^\d{4}-\d{2}-\d{2}$/.test(prop.min)) {
48
+ return prop.dateOnly ? prop.min : `${prop.min}T00:00`
49
+ }
50
+ return prop.min
51
+ })
39
52
  const inputPlaceholder = computed(() => (
40
53
  prop.placeholder || (prop.dateOnly ? 'MM/DD/YYYY' : 'DD/MM/YYYY, HH:MM AM/PM')
41
54
  ))
@@ -21,6 +21,9 @@
21
21
  :rules="[requiredRule, emailRule]"
22
22
  :loading="loading.verifyingEmail"
23
23
  />
24
+ <div v-if="existingUserMessage" class="text-subtitle-2 text-error mt-n3 mb-2">
25
+ {{ existingUserMessage }}
26
+ </div>
24
27
  </v-col>
25
28
 
26
29
  <!-- APP MULTI SELECT -->
@@ -37,7 +40,35 @@
37
40
  density="comfortable"
38
41
  :rules="[requiredRule]"
39
42
  @update:model-value="handleUpdateApp"
40
- />
43
+ >
44
+ <template #item="{ props: itemProps, item }">
45
+ <v-list-item
46
+ v-bind="itemProps"
47
+ :disabled="item.raw.disabled"
48
+ >
49
+ <template #prepend>
50
+ <v-checkbox-btn
51
+ :model-value="invite.app.includes(item.raw.value)"
52
+ :disabled="item.raw.disabled"
53
+ color="black"
54
+ density="comfortable"
55
+ />
56
+ </template>
57
+
58
+ <template #title>
59
+ <span :class="item.raw.disabled ? 'text-disabled' : ''">
60
+ {{ item.raw.title }}
61
+ </span>
62
+ </template>
63
+
64
+ <template #subtitle>
65
+ <span v-if="item.raw.disabled" class="text-error text-caption">
66
+ Already a member
67
+ </span>
68
+ </template>
69
+ </v-list-item>
70
+ </template>
71
+ </v-autocomplete>
41
72
  </v-col>
42
73
 
43
74
  <!-- ROLE -->
@@ -65,21 +96,11 @@
65
96
  />
66
97
  </v-col>
67
98
 
68
- <!-- CREATE MORE -->
69
- <v-col cols="12" class="mt-2">
70
- <v-checkbox v-model="createMore" density="comfortable" hide-details>
71
- <template #label>
72
- <span class="text-subtitle-2 font-weight-bold">
73
- Create more
74
- </span>
75
- </template>
76
- </v-checkbox>
77
- </v-col>
78
99
 
79
100
  <!-- ERROR -->
80
101
  <v-col cols="12" class="my-2 text-center">
81
102
  <span class="text-subtitle-2 text-error">
82
- {{ message }}
103
+ {{ submitError }}
83
104
  </span>
84
105
  </v-col>
85
106
 
@@ -116,14 +137,14 @@
116
137
  </template>
117
138
 
118
139
  <script setup lang="ts">
119
- import { computed, ref, reactive, watchEffect } from 'vue'
140
+ import { computed, ref, reactive, watchEffect, watch } from 'vue'
120
141
  import useCustomerSite from '../composables/useCustomerSite'
121
142
  import useLocal from '../composables/useLocal'
122
143
  import useRole from '../composables/useRole'
123
144
  import useUser from '../composables/useUser'
124
145
  import useUtils from '../composables/useUtils'
125
146
  import useOrg from '../composables/useOrg'
126
- import { watch } from 'vue'
147
+ import useMember from '../composables/useMember'
127
148
 
128
149
  const APP = useRuntimeConfig().public.APP
129
150
 
@@ -156,7 +177,6 @@ const invite = ref({
156
177
  isMemberInvite: false,
157
178
  })
158
179
 
159
- /* default app */
160
180
  if (props.mode === "create") {
161
181
  invite.value.app = props.app ? [props.app] : []
162
182
  }
@@ -168,11 +188,14 @@ const APP_LIST = [
168
188
  { title: "Cleaning Services", value: "cleaning_services" },
169
189
  { title: "Mechanical & Electrical Services", value: "mechanical_electrical_services" },
170
190
  { title: "Pest Control Services", value: "pest_control_services" },
191
+ { title: "Landscaping Services", value: "landscaping_services" },
171
192
  { title: "Pool Maintenance Services", value: "pool_maintenance_services" },
172
193
  ]
194
+
173
195
  const { getByName } = useOrg()
174
196
  const org = ref<any>(null)
175
- watch(
197
+
198
+ watch(
176
199
  () => props.org,
177
200
  async (name) => {
178
201
  if (!name) return
@@ -181,7 +204,14 @@ const org = ref<any>(null)
181
204
  { immediate: true }
182
205
  )
183
206
 
184
- const apps = computed(() => APP_LIST)
207
+ const apps = computed(() => {
208
+ return APP_LIST.map(app => {
209
+ const disabled = userAccess.value.some(
210
+ (a: any) => a.type === app.value
211
+ )
212
+ return { ...app, disabled }
213
+ })
214
+ })
185
215
 
186
216
  /* ================= SITE ================= */
187
217
  const { natureOfBusiness } = useLocal()
@@ -221,18 +251,14 @@ const { getRoles } = useRole()
221
251
 
222
252
  const { data: roleData, refresh: refreshRoles } = await useLazyAsyncData(
223
253
  "roles",
224
- () =>
225
- getRoles({
226
- org: org.value?._id,
227
- type: props.app,
228
- limit: 50,
229
- }),
254
+ () => getRoles({ org: org.value?._id, type: props.app, limit: 50 }),
230
255
  { watch: [() => props.app] }
231
256
  )
232
257
 
233
258
  watchEffect(() => {
234
259
  if (roleData.value) roles.value = roleData.value.items
235
260
  })
261
+
236
262
  watch(
237
263
  () => org.value?._id,
238
264
  async (id) => {
@@ -240,6 +266,7 @@ watch(
240
266
  await refreshRoles()
241
267
  }
242
268
  )
269
+
243
270
  /* ================= EVENTS ================= */
244
271
  async function handleUpdateApp(value: string[]) {
245
272
  invite.value.role = ""
@@ -252,59 +279,86 @@ function handleUpdateSite(id: string) {
252
279
  invite.value.siteName = obj?.title || ""
253
280
  }
254
281
 
255
- /* ================= SUBMIT ================= */
256
- const { inviteUserClient } = useUser()
282
+ /* ================= EMAIL CHECK ================= */
283
+ const { inviteUserClient, getUserByEmail } = useUser()
284
+ const { getAllByUserId } = useMember()
257
285
  const { requiredRule, emailRule } = useUtils()
258
286
 
259
- const message = ref("")
260
- const createMore = ref(false)
287
+ const existingUserMessage = ref("")
288
+ const submitError = ref("")
261
289
  const disable = ref(false)
290
+ const existingUser = ref<any>(null)
291
+ const userAccess = ref<any[]>([])
262
292
 
263
- async function submit() {
264
- loading.submittingForm = true
293
+ let timeout: any = null
294
+
295
+ watch(
296
+ () => invite.value.email,
297
+ (email) => {
298
+ clearTimeout(timeout)
299
+ existingUserMessage.value = ""
300
+ existingUser.value = null
301
+ userAccess.value = []
302
+
303
+ if (!email || email.length < 3) return
304
+
305
+ timeout = setTimeout(async () => {
306
+ await checkEmail(email)
307
+ }, 400)
308
+ }
309
+ )
310
+
311
+ async function checkEmail(email: string) {
312
+ loading.verifyingEmail = true
313
+ existingUserMessage.value = ""
265
314
 
266
315
  try {
267
- const apps = invite.value.app || []
268
-
269
- await Promise.all(
270
- apps.map(app =>
271
- inviteUserClient({
272
- email: invite.value.email,
273
- app,
274
- role: invite.value.role,
275
- // org: invite.value.org,
276
- org: org.value?._id,
277
- site: invite.value.site,
278
- siteName: invite.value.siteName,
279
- // isMemberInvite: invite.value.isMemberInvite,
280
- })
281
- )
282
- )
316
+ const user = await getUserByEmail(email)
317
+
318
+ if (user?._id) {
319
+ existingUser.value = user
320
+ existingUserMessage.value = `User already exists: ${email}`
321
+
322
+ const memberRes = await getAllByUserId(user._id)
283
323
 
284
- if (createMore.value) {
285
- form.value?.reset()
286
- invite.value = {
287
- email: "",
288
- app: [],
289
- role: "",
290
- org: org.value?._id || "",
291
- site: "",
292
- siteName: "",
293
- isMemberInvite: false,
294
- }
295
- emit("success", false)
324
+ userAccess.value = memberRes?.items || []
296
325
  } else {
297
- emit("success", true)
326
+ existingUser.value = null
327
+ userAccess.value = []
298
328
  }
329
+ } catch (e) {
330
+ existingUser.value = null
331
+ userAccess.value = []
332
+ } finally {
333
+ loading.verifyingEmail = false
334
+ }
335
+ }
336
+
337
+ /* ================= SUBMIT ================= */
338
+ async function submit() {
339
+ loading.submittingForm = true
340
+ submitError.value = ""
341
+
342
+ try {
343
+ await inviteUserClient({
344
+ email: invite.value.email,
345
+ app: invite.value.app,
346
+ role: invite.value.role,
347
+ org: org.value?._id,
348
+ site: invite.value.site,
349
+ siteName: invite.value.siteName,
350
+ })
351
+
352
+ emit("success", true)
299
353
 
300
354
  } catch (e: any) {
301
- message.value = e?.response?._data?.message || "Error"
355
+ submitError.value = e?.response?._data?.message || "Error"
302
356
  } finally {
303
357
  loading.submittingForm = false
304
358
  }
305
359
  }
306
-
307
360
  function cancel() {
308
361
  emit("cancel")
309
362
  }
310
363
  </script>
364
+
@@ -190,6 +190,10 @@ const props = defineProps({
190
190
  hideApp: {
191
191
  type: Boolean,
192
192
  default: false
193
+ },
194
+ inviteMember: {
195
+ type: Boolean,
196
+ default: false
193
197
  }
194
198
  });
195
199
 
@@ -377,20 +381,27 @@ function handleUpdateSite(siteId: string) {
377
381
  invite.value.siteName = obj?.title || "";
378
382
  }
379
383
 
380
- const { inviteUser } = useUser();
384
+ const { inviteUser, inviteMemberClient } = useUser();
381
385
 
382
386
  async function submit() {
383
387
  loading.submittingForm = true;
388
+
384
389
  try {
385
- await inviteUser(invite.value);
390
+ if (props.inviteMember) {
391
+ await inviteMemberClient(invite.value);
392
+ } else {
393
+ await inviteUser(invite.value);
394
+ }
386
395
 
387
396
  if (createMore.value) {
388
397
  form.value?.reset();
389
398
  resetInvite();
390
399
  emit("success", false);
391
- } else emit("success", true);
400
+ } else {
401
+ emit("success", true);
402
+ }
392
403
  } catch (error: any) {
393
- message.value = error.response._data.message;
404
+ message.value = error.response?._data?.message || error.message;
394
405
  } finally {
395
406
  loading.submittingForm = false;
396
407
  }