@iservice365/layer-common 1.0.8 → 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,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.0.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 681b0f4: Update dependencies - vuetify
8
+
3
9
  ## 1.0.8
4
10
 
5
11
  ### 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
@@ -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>
@@ -64,6 +65,7 @@
64
65
  :items="sites"
65
66
  density="comfortable"
66
67
  :rules="[requiredRule]"
68
+ @update:model-value="handleUpdateSite"
67
69
  ></v-autocomplete>
68
70
  </v-col>
69
71
  </v-row>
@@ -118,7 +120,7 @@
118
120
  class="text-none"
119
121
  size="48"
120
122
  :disabled="!validForm"
121
- :loading="submitting"
123
+ :loading="loading.submittingForm"
122
124
  @click="submit"
123
125
  >
124
126
  Submit
@@ -176,7 +178,11 @@ const emit = defineEmits(["cancel", "success", "success:create-more"]);
176
178
  const validForm = ref(false);
177
179
  const form = ref<HTMLFormElement | null>(null)
178
180
  const app = computed(() => useRuntimeConfig().public.APP ?? "");
179
- const submitting = ref(false);
181
+
182
+ const loading = reactive({
183
+ submittingForm: false,
184
+ verifyingEmail: false
185
+ })
180
186
 
181
187
  const invite = ref<Record<string, any>>({
182
188
  email: "",
@@ -279,7 +285,7 @@ function handleUpdateApp(value: string){
279
285
  const createMore = ref(false);
280
286
  const disable = ref(false);
281
287
 
282
- const { requiredRule } = useUtils();
288
+ const { requiredRule, emailRule } = useUtils();
283
289
 
284
290
  const message = ref("");
285
291
 
@@ -290,10 +296,16 @@ function resetInvite() {
290
296
  message.value = "";
291
297
  }
292
298
 
299
+ function handleUpdateSite(siteId: string){
300
+ const obj = sites.value.find( x => x?.value === siteId)
301
+ invite.value.siteName = obj?.title || ""
302
+ }
303
+
293
304
  const { inviteUser } = useUser();
294
305
 
306
+
295
307
  async function submit() {
296
- submitting.value = true;
308
+ loading.submittingForm = true;
297
309
  try {
298
310
  await inviteUser(invite.value);
299
311
 
@@ -305,7 +317,7 @@ async function submit() {
305
317
  } catch (error: any) {
306
318
  message.value = error.response._data.message;
307
319
  } finally {
308
- submitting.value = false;
320
+ loading.submittingForm = false;
309
321
  }
310
322
  }
311
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>
@@ -244,7 +244,8 @@ const {
244
244
  page: page.value,
245
245
  status: props.status,
246
246
  search: headerSearch.value,
247
- type: "user-invite",
247
+ type: "user-invite,member-invite",
248
+ app: props.app
248
249
  })
249
250
  );
250
251
 
@@ -258,7 +259,7 @@ watchEffect(() => {
258
259
  }
259
260
  });
260
261
 
261
- watch([page, headerSearch], () => {
262
+ watch([headerSearch], () => {
262
263
  getVerifications();
263
264
  });
264
265
 
@@ -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>
@@ -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
 
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.8",
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": {
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 = {