@iservice365/layer-common 1.0.7 → 1.0.9

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.0.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 681b0f4: Update dependencies - vuetify
8
+
9
+ ## 1.0.8
10
+
11
+ ### Patch Changes
12
+
13
+ - 2ba3c2e: Invitation revision - add site support
14
+
3
15
  ## 1.0.7
4
16
 
5
17
  ### Patch Changes
@@ -19,7 +19,7 @@
19
19
  </v-col>
20
20
 
21
21
  <v-col cols="12" class="text-center mb-3" v-if="item">
22
- <v-avatar
22
+ <!-- <v-avatar
23
23
  size="48"
24
24
  class="mb-1"
25
25
  v-if="!item.attachments || !item.attachments.length"
@@ -50,7 +50,33 @@
50
50
  v-for="(attachment, index) in item.attachments"
51
51
  :key="index"
52
52
  >
53
- <v-img :src="attachment" cover class="w-100 h-100" />
53
+ <v-img :src="`/api/public/${attachment}`" cover class="w-100 h-100" />
54
+ </v-carousel-item>
55
+ </v-carousel> -->
56
+ <v-img
57
+ v-if="item.attachments.length === 1"
58
+ :src="`/api/public/${item.attachments[0]}`"
59
+ height="200"
60
+ cover
61
+ class="rounded mb-2"
62
+ />
63
+ <v-carousel
64
+ v-else
65
+ hide-delimiter-background
66
+ height="200"
67
+ class="rounded mb-2"
68
+ show-arrows
69
+ cycle
70
+ >
71
+ <v-carousel-item
72
+ v-for="(attachment, index) in item.attachments"
73
+ :key="index"
74
+ >
75
+ <v-img
76
+ :src="`/api/public/${attachment}`"
77
+ cover
78
+ class="w-100 h-100"
79
+ />
54
80
  </v-carousel-item>
55
81
  </v-carousel>
56
82
  </v-col>
@@ -58,7 +84,9 @@
58
84
  <v-col cols="12" v-if="item">
59
85
  <v-row dense class="my-1">
60
86
  <v-col cols="6" class="py-1"><strong>Category:</strong></v-col>
61
- <v-col cols="6" class="py-1 text-right text-capitalize">{{ item.category }}</v-col>
87
+ <v-col cols="6" class="py-1 text-right text-capitalize">{{
88
+ item.category
89
+ }}</v-col>
62
90
  </v-row>
63
91
 
64
92
  <v-divider />
@@ -1,28 +1,39 @@
1
1
  <template>
2
- <v-row no-gutters class="fill-height" align="stretch">
3
- <v-col cols="12" md="3" class="fill-height">
4
- <ChatNavigation
5
- :title="'Feedbacks'"
6
- :items="items"
7
- @select="handleSelectFeedback"
8
- @search="_getFeedbacks"
9
- />
10
- </v-col>
11
-
12
- <v-col cols="12" md="6" class="fill-height">
13
- <ChatMessage />
14
- </v-col>
15
-
16
- <v-col cols="12" md="3" class="fill-height">
17
- <ChatInformation
18
- :item="feedback"
19
- :service-providers="_serviceProviders"
20
- @edit="openEditDialog"
21
- @mark-complete-request="showCompleteDialog = true"
22
- @delete="showDeleteDialog = true"
23
- />
24
- </v-col>
25
- </v-row>
2
+ <div class="feedback-detail-wrapper">
3
+ <v-row no-gutters class="fill-height">
4
+
5
+ <v-col cols="12" xl="3" lg="4" md="4" class="fill-height">
6
+ <div class="panel-container border-e">
7
+ <ChatNavigation
8
+ :title="'Feedbacks'"
9
+ :items="items"
10
+ @select="handleSelectFeedback"
11
+ @search="_getFeedbacks"
12
+ />
13
+ </div>
14
+ </v-col>
15
+
16
+
17
+ <v-col cols="12" xl="6" lg="5" md="5" class="fill-height">
18
+ <div class="panel-container border-e">
19
+ <ChatMessage />
20
+ </div>
21
+ </v-col>
22
+
23
+
24
+ <v-col cols="12" xl="3" lg="3" md="3" class="fill-height">
25
+ <div class="panel-container">
26
+ <ChatInformation
27
+ :item="feedback"
28
+ :service-providers="_serviceProviders"
29
+ @edit="openEditDialog"
30
+ @mark-complete-request="showCompleteDialog = true"
31
+ @delete="showDeleteDialog = true"
32
+ />
33
+ </div>
34
+ </v-col>
35
+ </v-row>
36
+ </div>
26
37
 
27
38
  <FeedbackForm
28
39
  v-model="showCreateDialog"
@@ -463,3 +474,118 @@ async function submitFeedback(payload: TFeedbackUpdate) {
463
474
  }
464
475
  }
465
476
  </script>
477
+
478
+ <style scoped>
479
+ .feedback-detail-wrapper {
480
+ height: calc(100vh - 80px); /* Account for header and padding */
481
+ max-height: calc(100vh - 80px);
482
+ overflow: hidden;
483
+ position: relative;
484
+ background-color: rgb(var(--v-theme-surface));
485
+ }
486
+
487
+ .panel-container {
488
+ height: 100%;
489
+ max-height: 100%;
490
+ overflow: hidden;
491
+ display: flex;
492
+ flex-direction: column;
493
+ background-color: rgb(var(--v-theme-surface));
494
+ }
495
+
496
+ .border-e {
497
+ border-right: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
498
+ }
499
+
500
+ .fill-height {
501
+ height: 100%;
502
+ max-height: 100%;
503
+ overflow: hidden;
504
+ }
505
+
506
+ /* Ensure the root row doesn't create scrolling */
507
+ .v-row.fill-height {
508
+ margin: 0;
509
+ height: 100%;
510
+ max-height: 100%;
511
+ overflow: hidden;
512
+ align-items: stretch;
513
+ }
514
+
515
+ /* Better column spacing */
516
+ .v-col {
517
+ padding: 0;
518
+ display: flex;
519
+ flex-direction: column;
520
+ }
521
+
522
+ /* Ensure responsive behavior */
523
+ @media (max-width: 1280px) {
524
+ .feedback-detail-wrapper {
525
+ height: calc(100vh - 72px);
526
+ max-height: calc(100vh - 72px);
527
+ }
528
+ }
529
+
530
+ @media (max-width: 960px) {
531
+ .feedback-detail-wrapper {
532
+ height: calc(100vh - 64px); /* Mobile header height */
533
+ max-height: calc(100vh - 64px);
534
+ }
535
+
536
+ .border-e {
537
+ border-right: none;
538
+ border-bottom: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
539
+ }
540
+
541
+ .v-col {
542
+ height: auto;
543
+ min-height: 300px;
544
+ }
545
+ }
546
+
547
+ /* Individual component styling */
548
+ :deep(.chat-navigation) {
549
+ height: 100%;
550
+ overflow-y: auto;
551
+ background-color: rgb(var(--v-theme-surface));
552
+ }
553
+
554
+ :deep(.chat-message) {
555
+ height: 100%;
556
+ overflow-y: auto;
557
+ background-color: rgb(var(--v-theme-surface));
558
+ }
559
+
560
+ :deep(.chat-information) {
561
+ height: 100%;
562
+ overflow-y: auto;
563
+ background-color: rgb(var(--v-theme-surface));
564
+ padding: 16px;
565
+ }
566
+
567
+ /* Smooth scrollbars */
568
+ :deep(*::-webkit-scrollbar) {
569
+ width: 6px;
570
+ }
571
+
572
+ :deep(*::-webkit-scrollbar-track) {
573
+ background: transparent;
574
+ }
575
+
576
+ :deep(*::-webkit-scrollbar-thumb) {
577
+ background: rgba(var(--v-border-color), 0.3);
578
+ border-radius: 3px;
579
+ }
580
+
581
+ :deep(*::-webkit-scrollbar-thumb:hover) {
582
+ background: rgba(var(--v-border-color), 0.5);
583
+ }
584
+
585
+ /* Remove default margins/padding that might cause issues */
586
+ :deep(.v-container) {
587
+ padding: 0;
588
+ margin: 0;
589
+ max-width: none;
590
+ }
591
+ </style>
@@ -424,7 +424,7 @@ async function handleFileAdded(file: File) {
424
424
 
425
425
  const uploadedId = res?.id;
426
426
  if (uploadedId) {
427
- const url = `${API_DO_STORAGE_ENDPOINT}/${uploadedId}`;
427
+ const url = `${uploadedId}`;
428
428
  _feedback.value.attachments = _feedback.value.attachments ?? [];
429
429
  _feedback.value.attachments.push(url);
430
430
  }
@@ -60,7 +60,7 @@
60
60
  class="d-flex align-center pa-2 mr-2 mb-2 rounded bg-white border-sm"
61
61
  >
62
62
  <div class="mr-3">
63
- <v-img
63
+ <!-- <v-img
64
64
  v-if="!localErroredImages.includes(file)"
65
65
  :src="file"
66
66
  width="40"
@@ -76,6 +76,13 @@
76
76
  height="40"
77
77
  class="rounded"
78
78
  cover
79
+ /> -->
80
+ <v-img
81
+ :src="getThumbnail(file)"
82
+ width="40"
83
+ height="40"
84
+ class="rounded"
85
+ cover
79
86
  />
80
87
  </div>
81
88
 
@@ -166,12 +173,13 @@ function onImageError(file: string) {
166
173
  }
167
174
 
168
175
  function getThumbnail(fileUrl: string): string {
169
- if (fileUrl.endsWith(".pdf")) return "mdi-file-pdf-outline";
170
- if (fileUrl.match(/\.(doc|docx)$/i))
171
- return "/images/file-thumbnails/word.png";
172
- if (fileUrl.match(/\.(xls|xlsx)$/i))
173
- return "/images/file-thumbnails/excel.png";
174
- return "/images/file-thumbnails/file.png";
176
+ // if (fileUrl.endsWith(".pdf")) return "mdi-file-pdf-outline";
177
+ // if (fileUrl.match(/\.(doc|docx)$/i))
178
+ // return "/images/file-thumbnails/word.png";
179
+ // if (fileUrl.match(/\.(xls|xlsx)$/i))
180
+ // return "/images/file-thumbnails/excel.png";
181
+ // return "/images/file-thumbnails/file.png";
182
+ return `/api/public/${fileUrl}`
175
183
  }
176
184
 
177
185
  // Modified to try to display the friendly name
@@ -8,7 +8,7 @@
8
8
  </v-row>
9
9
  </v-toolbar>
10
10
  <v-card-text style="max-height: 100vh; overflow-y: auto">
11
- <v-form v-model="validForm" :disabled="disable">
11
+ <v-form v-model="validForm" ref="form" :disabled="disable">
12
12
  <v-row no-gutters>
13
13
  <v-col cols="12" class="mt-2">
14
14
  <v-row no-gutters>
@@ -17,7 +17,8 @@
17
17
  <v-text-field
18
18
  v-model="invite.email"
19
19
  density="comfortable"
20
- :rules="[requiredRule]"
20
+ :rules="[requiredRule, emailRule]"
21
+ :loading="loading.verifyingEmail"
21
22
  ></v-text-field>
22
23
  </v-col>
23
24
  </v-row>
@@ -32,6 +33,7 @@
32
33
  density="comfortable"
33
34
  :rules="[requiredRule]"
34
35
  :items="apps"
36
+ @update:model-value="handleUpdateApp"
35
37
  ></v-autocomplete>
36
38
  </v-col>
37
39
  </v-row>
@@ -46,7 +48,24 @@
46
48
  :items="roles"
47
49
  item-title="name"
48
50
  item-value="_id"
51
+ :rules="[requiredRule]"
52
+ density="comfortable"
53
+ ></v-autocomplete>
54
+ </v-col>
55
+ </v-row>
56
+ </v-col>
57
+
58
+
59
+ <v-col v-if="hasSite" cols="12">
60
+ <v-row no-gutters>
61
+ <InputLabel class="text-capitalize" title="Site" />
62
+ <v-col cols="12">
63
+ <v-autocomplete
64
+ v-model="invite.site"
65
+ :items="sites"
49
66
  density="comfortable"
67
+ :rules="[requiredRule]"
68
+ @update:model-value="handleUpdateSite"
50
69
  ></v-autocomplete>
51
70
  </v-col>
52
71
  </v-row>
@@ -101,6 +120,7 @@
101
120
  class="text-none"
102
121
  size="48"
103
122
  :disabled="!validForm"
123
+ :loading="loading.submittingForm"
104
124
  @click="submit"
105
125
  >
106
126
  Submit
@@ -126,9 +146,9 @@ const props = defineProps({
126
146
  type: Object,
127
147
  default: () => ({}),
128
148
  },
129
- type: {
149
+ app: {
130
150
  type: String,
131
- default: "app",
151
+ default: "organization",
132
152
  },
133
153
  org: {
134
154
  type: String,
@@ -156,16 +176,24 @@ const props = defineProps({
156
176
  const emit = defineEmits(["cancel", "success", "success:create-more"]);
157
177
 
158
178
  const validForm = ref(false);
179
+ const form = ref<HTMLFormElement | null>(null)
159
180
  const app = computed(() => useRuntimeConfig().public.APP ?? "");
160
181
 
182
+ const loading = reactive({
183
+ submittingForm: false,
184
+ verifyingEmail: false
185
+ })
186
+
161
187
  const invite = ref<Record<string, any>>({
162
188
  email: "",
163
- app: "organization",
189
+ app: "",
164
190
  role: "",
165
191
  org: "",
192
+ site: "",
193
+ siteName: "",
166
194
  });
167
195
 
168
- invite.value.app = app.value ?? "organization";
196
+ invite.value.app = props.app;
169
197
  invite.value.org = props.org ?? "";
170
198
 
171
199
  if (props.mode === "edit") {
@@ -177,16 +205,65 @@ if (props.mode === "edit") {
177
205
  const { natureOfBusiness } = useLocal();
178
206
 
179
207
  const apps = computed(() => {
180
- const items = natureOfBusiness.slice(1);
208
+ const items = [];
181
209
  items.unshift({ title: "Organization", value: "organization" });
210
+
211
+ if (props.app === "security_agency") {
212
+ items.push({ title: "Security Agency", value: "security_agency" });
213
+ }
214
+
215
+ if (props.app === "cleaning_agency") {
216
+ items.push({ title: "Cleaning Agency", value: "cleaning_agency" });
217
+ }
218
+
219
+ if (props.app === "property_manager") {
220
+ items.push({ title: "Property Manager", value: "property_manager" });
221
+ }
222
+
223
+ if (props.app === "mechanical_electrical_services") {
224
+ items.push({
225
+ title: "Mechanical & Electrical Services",
226
+ value: "mechanical_electrical_services",
227
+ });
228
+ }
229
+
182
230
  return items;
183
231
  });
184
232
 
233
+ const hasSite = computed(() => {
234
+ return natureOfBusiness.map((i) => i.value).includes(invite.value.app);
235
+ });
236
+
237
+ const sites = ref<Array<Record<string, any>>>([]);
238
+
239
+ const { getAll: getAllCustomerSite } = useCustomerSite();
240
+
241
+ const { data: siteData, refresh: refreshSiteData } = await useLazyAsyncData(
242
+ "get-sites-by-org",
243
+ async () => await getAllCustomerSite({ org: props.org, limit: 50 }),
244
+ { }
245
+ );
246
+
247
+ watchEffect(() => {
248
+ if (siteData.value) {
249
+ sites.value = siteData.value.items.map((i: any) => ({
250
+ title: i.name,
251
+ value: i.site,
252
+ }));
253
+ }
254
+ });
255
+
256
+ watchEffect(() => {
257
+ if (hasSite.value) {
258
+ refreshSiteData();
259
+ }
260
+ });
261
+
185
262
  const roles = ref<Array<Record<string, any>>>([]);
186
263
 
187
264
  const { getRoles } = useRole();
188
265
 
189
- const { data: RolesData } = await useLazyAsyncData(
266
+ const { data: RolesData, refresh: refreshRoles } = await useLazyAsyncData(
190
267
  "get-roles-by-type",
191
268
  () => getRoles({ org: props.org, type: app.value, limit: 50 }),
192
269
  { watch: [app] }
@@ -198,17 +275,17 @@ watchEffect(() => {
198
275
  }
199
276
  });
200
277
 
201
- watchEffect(() => {
202
- if (invite.value.app) {
203
- roles.value = [];
204
- invite.value.role = "";
205
- }
206
- });
278
+ function handleUpdateApp(value: string){
279
+ invite.value.role = "";
280
+ invite.value.site = "";
281
+ refreshRoles();
282
+ }
283
+
207
284
 
208
285
  const createMore = ref(false);
209
286
  const disable = ref(false);
210
287
 
211
- const { requiredRule } = useUtils();
288
+ const { requiredRule, emailRule } = useUtils();
212
289
 
213
290
  const message = ref("");
214
291
 
@@ -219,18 +296,28 @@ function resetInvite() {
219
296
  message.value = "";
220
297
  }
221
298
 
299
+ function handleUpdateSite(siteId: string){
300
+ const obj = sites.value.find( x => x?.value === siteId)
301
+ invite.value.siteName = obj?.title || ""
302
+ }
303
+
222
304
  const { inviteUser } = useUser();
223
305
 
306
+
224
307
  async function submit() {
308
+ loading.submittingForm = true;
225
309
  try {
226
310
  await inviteUser(invite.value);
227
- emit("success");
228
311
 
229
312
  if (createMore.value) {
313
+ form.value?.reset();
230
314
  resetInvite();
231
- }
315
+ emit("success", false);
316
+ } else emit("success", true);
232
317
  } catch (error: any) {
233
318
  message.value = error.response._data.message;
319
+ } finally {
320
+ loading.submittingForm = false;
234
321
  }
235
322
  }
236
323
 
@@ -36,7 +36,7 @@
36
36
  <local-pagination
37
37
  v-model="page"
38
38
  :length="pages"
39
- @update:value="_getVerifications({ search: headerSearch })"
39
+ @update:value="getVerifications"
40
40
  />
41
41
  </v-row>
42
42
  </template>
@@ -142,7 +142,9 @@
142
142
  title="Invite member"
143
143
  :org="org"
144
144
  @cancel="dialogMember = false"
145
+ @success="handleSuccess"
145
146
  @invited="getVerifications()"
147
+ :app="props.app"
146
148
  />
147
149
  </v-dialog>
148
150
  </v-row>
@@ -150,6 +152,10 @@
150
152
 
151
153
  <script setup lang="ts">
152
154
  const props = defineProps({
155
+ app: {
156
+ type: String,
157
+ default: "organization",
158
+ },
153
159
  route: {
154
160
  type: String,
155
161
  default: "index",
@@ -238,7 +244,8 @@ const {
238
244
  page: page.value,
239
245
  status: props.status,
240
246
  search: headerSearch.value,
241
- type: "user-invite",
247
+ type: "user-invite,member-invite",
248
+ app: props.app
242
249
  })
243
250
  );
244
251
 
@@ -252,7 +259,7 @@ watchEffect(() => {
252
259
  }
253
260
  });
254
261
 
255
- watch([page, headerSearch], () => {
262
+ watch([headerSearch], () => {
256
263
  getVerifications();
257
264
  });
258
265
 
@@ -282,6 +289,13 @@ async function onConfirmCancel() {
282
289
  cancelLoading.value = false;
283
290
  }
284
291
 
292
+ function handleSuccess(closeForm: boolean) {
293
+ getVerifications();
294
+ if (closeForm) {
295
+ dialogMember.value = false;
296
+ }
297
+ }
298
+
285
299
  watchEffect(() => {
286
300
  if (!props.viewInvitations) {
287
301
  useRouter().back();
@@ -4,14 +4,8 @@
4
4
  <v-col cols="12" class="mb-2" v-if="canCreate || $slots.actions">
5
5
  <v-row no-gutters>
6
6
  <slot name="actions">
7
- <v-btn
8
- v-if="canCreate"
9
- class="text-none"
10
- rounded="pill"
11
- variant="tonal"
12
- size="large"
13
- @click="emits('create')"
14
- >
7
+ <v-btn v-if="canCreate" class="text-none" rounded="pill" variant="tonal" size="large"
8
+ @click="emits('create')">
15
9
  {{ createLabel }}
16
10
  </v-btn>
17
11
  </slot>
@@ -20,13 +14,7 @@
20
14
 
21
15
  <!-- Table Card -->
22
16
  <v-col cols="12">
23
- <v-card
24
- width="100%"
25
- variant="outlined"
26
- border="thin"
27
- rounded="lg"
28
- :loading="loading"
29
- >
17
+ <v-card width="100%" variant="outlined" border="thin" rounded="lg" :loading="loading">
30
18
  <!-- Toolbar -->
31
19
  <v-toolbar density="compact" color="grey-lighten-4">
32
20
  <template #prepend>
@@ -40,27 +28,20 @@
40
28
  <span class="mr-2 text-caption text-fontgray">
41
29
  {{ pageRange }}
42
30
  </span>
43
- <local-pagination
44
- v-model="internalPage"
45
- :length="pages"
46
- @update:value="emits('update:page', internalPage)"
47
- />
31
+ <local-pagination v-model="internalPage" :length="pages"
32
+ @update:value="emits('update:page', internalPage)" />
48
33
  </v-row>
49
34
  </template>
35
+
36
+ <template v-if="$slots.extension" #extension>
37
+ <slot name="extension" />
38
+ </template>
50
39
  </v-toolbar>
51
40
 
52
41
  <!-- Data Table -->
53
- <v-data-table
54
- :headers="headers"
55
- :items="items"
56
- :item-value="itemValue"
57
- :items-per-page="itemsPerPage"
58
- fixed-header
59
- hide-default-footer
60
- hide-default-header
61
- @click:row="(_: any, data: any) => emits('row-click', data)"
62
- style="max-height: calc(100vh - (200px))"
63
- >
42
+ <v-data-table :headers="headers" :items="items" :item-value="itemValue" :items-per-page="itemsPerPage"
43
+ fixed-header hide-default-footer hide-default-header
44
+ @click:row="(_: any, data: any) => emits('row-click', data)" style="max-height: calc(100vh - (200px))">
64
45
  <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
65
46
  <slot :name="slotName" v-bind="slotProps" />
66
47
  </template>
@@ -3,7 +3,7 @@ export function useLocalSetup() {
3
3
 
4
4
  const id = useState<string | null>("memberShipOrgId", () => null);
5
5
 
6
- const orgNature = useState<string | null>("orgNature", () => null);
6
+ const orgNature = useState<string>("orgNature", () => "");
7
7
 
8
8
  return {
9
9
  userAppRole,
@@ -5,10 +5,12 @@ export default function useUser() {
5
5
  role = "",
6
6
  name = "",
7
7
  org = "",
8
+ site="",
9
+ siteName="",
8
10
  } = {}) {
9
11
  return useNuxtApp().$api<Record<string, any>>("/api/auth/invite", {
10
12
  method: "POST",
11
- body: { email, app, role, name, org },
13
+ body: { email, app, role, name, org, ...(site && {siteId: site}), ...(siteName && {siteName}) },
12
14
  });
13
15
  }
14
16
 
@@ -106,6 +108,12 @@ export default function useUser() {
106
108
  });
107
109
  }
108
110
 
111
+ function getUserByEmail(email = "") {
112
+ return useNuxtApp().$api<Record<string, any>>(`/api/users/email/${email}`, {
113
+ method: "GET"
114
+ });
115
+ }
116
+
109
117
  return {
110
118
  inviteUser,
111
119
  updateName,
@@ -119,5 +127,6 @@ export default function useUser() {
119
127
  createUserByInvite,
120
128
  getById,
121
129
  createUserByVerification,
130
+ getUserByEmail
122
131
  };
123
132
  }
@@ -5,6 +5,7 @@ export default function useVerification() {
5
5
  search = "",
6
6
  page = 1,
7
7
  email = "",
8
+ app = ""
8
9
  } = {}): Promise<{
9
10
  items: TMiniVerification[];
10
11
  pages: number;
@@ -12,7 +13,7 @@ export default function useVerification() {
12
13
  }> {
13
14
  return useNuxtApp().$api("/api/verifications", {
14
15
  method: "GET",
15
- query: { status, search, page, type, email },
16
+ query: { status, search, page, type, email, app },
16
17
  });
17
18
  }
18
19
 
@@ -4,12 +4,12 @@ const hexSchema = z
4
4
  .string()
5
5
  .regex(/^[0-9a-fA-F]{24}$/, "Invalid organization ID");
6
6
 
7
- export default defineNuxtRouteMiddleware(async (to) => {
7
+ export default defineNuxtRouteMiddleware(async (to, from) => {
8
8
  if (import.meta.server) return;
9
9
 
10
10
  const { organization, org } = to.params;
11
11
 
12
- if (!hexSchema.safeParse(organization ?? org).success) {
12
+ if (!hexSchema.safeParse(organization || org).success) {
13
13
  return navigateTo(
14
14
  { name: "require-organization-membership" },
15
15
  { replace: true }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@iservice365/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.0.7",
5
+ "version": "1.0.9",
6
6
  "main": "./nuxt.config.ts",
7
7
  "scripts": {
8
8
  "dev": "nuxi dev .playground",
@@ -20,7 +20,7 @@
20
20
  "typescript": "^5.8.3",
21
21
  "vite-plugin-vuetify": "^2.0.4",
22
22
  "vue": "latest",
23
- "vuetify": "^3.7.3"
23
+ "vuetify": "^3.10.4"
24
24
  },
25
25
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
26
26
  "dependencies": {
@@ -6,7 +6,7 @@ export default defineNuxtPlugin(() => {
6
6
 
7
7
  const { userAppRole, id, orgNature } = useLocalSetup();
8
8
 
9
- router.afterEach((to) => {
9
+ router.afterEach(async (to) => {
10
10
  const isMember = to.meta?.memberOnly;
11
11
 
12
12
  if (!isMember) return;
@@ -19,11 +19,12 @@ export default defineNuxtPlugin(() => {
19
19
 
20
20
  const userId = computed(() => useCookie("user").value ?? "");
21
21
 
22
- const { data: userMemberData, error: userMemberError } = useLazyAsyncData(
23
- "get-member-by-id",
24
- () => getByUserType(userId.value, APP, org.value),
25
- { watch: [userId] }
26
- );
22
+ const { data: userMemberData, error: userMemberError } =
23
+ await useLazyAsyncData(
24
+ "get-member-by-id",
25
+ () => getByUserType(userId.value, APP, org.value),
26
+ { watch: [userId] }
27
+ );
27
28
 
28
29
  watchEffect(() => {
29
30
  if (userMemberError.value) {
@@ -42,7 +43,7 @@ export default defineNuxtPlugin(() => {
42
43
  }
43
44
  });
44
45
 
45
- const { data: getOrgByIdReq } = useLazyAsyncData(
46
+ const { data: getOrgByIdReq } = await useLazyAsyncData(
46
47
  "get-org-by-id",
47
48
  () => getById(org.value),
48
49
  { watch: [org] }
@@ -56,7 +57,7 @@ export default defineNuxtPlugin(() => {
56
57
 
57
58
  const roleId = computed(() => userMemberData.value?.role ?? "");
58
59
 
59
- const { data: getRoleByIdReq } = useLazyAsyncData(
60
+ const { data: getRoleByIdReq } = await useLazyAsyncData(
60
61
  "get-role-by-id",
61
62
  () => getRoleById(roleId.value),
62
63
  { watch: [roleId], immediate: false }
package/types/local.d.ts CHANGED
@@ -7,6 +7,7 @@ declare type TToken = {
7
7
  declare type TNavigationRoute = {
8
8
  name: string;
9
9
  params?: TKeyValuePair;
10
+ query?: TKeyValuePair;
10
11
  };
11
12
 
12
13
  declare type TNavigationItem = {