@7365admin1/layer-common 1.11.32 → 1.11.33

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.11.33
4
+
5
+ ### Patch Changes
6
+
7
+ - ed63329: Update Layer-common version
8
+
3
9
  ## 1.11.32
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,228 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+
4
+ <!-- MAIN TABLE -->
5
+ <v-col cols="12">
6
+ <TableMain :headers="currentHeaders" :items="currentItems" :loading="loadingState" :page="currentPage"
7
+ :pages="currentPages" :pageRange="pageRange" :extension-height="120" :offset="300" show-header
8
+ @refresh="handleRefresh" @update:page="handleUpdatePage">
9
+
10
+ <!-- EXTENSION -->
11
+ <template #extension>
12
+ <v-row no-gutters class="w-100 d-flex flex-column ga-2 pt-2">
13
+
14
+ <v-tabs v-model="tab" class="w-100" height="32" @update:model-value="onTabChange">
15
+ <v-tab value="active">Active</v-tab>
16
+ <v-tab value="suspended">Suspended</v-tab>
17
+ <v-tab value="pending">Pending Invitation</v-tab>
18
+ </v-tabs>
19
+
20
+ <div class="px-3 py-2 w-100">
21
+ <v-text-field v-model="search" density="compact" placeholder="Search" clearable max-width="300"
22
+ append-inner-icon="mdi-magnify" hide-details @update:model-value="handleSearch" />
23
+ </div>
24
+
25
+ </v-row>
26
+ </template>
27
+
28
+ <!-- ACTIONS (ONLY ONE INVITE BUTTON HERE) -->
29
+ <template #actions>
30
+ <v-btn class="text-none" rounded="pill" variant="tonal" size="large" @click="openInviteDialog">
31
+ Invite Client
32
+ </v-btn>
33
+ </template>
34
+
35
+ <!-- CUSTOM CELL -->
36
+ <template #item.index="{ index }">
37
+ {{ (currentPage - 1) * 10 + index + 1 }}
38
+ </template>
39
+
40
+ <template #item.status="{ value }">
41
+ <v-chip size="small" variant="tonal">
42
+ {{ value }}
43
+ </v-chip>
44
+ </template>
45
+ <template #item.action="{ item }">
46
+ <v-btn v-if="tab === 'pending'" size="small" color="error" variant="tonal"
47
+ :loading="cancelLoadingId === item._id" :disabled="cancelLoadingId === item._id"
48
+ @click="cancelInvite(item)">
49
+ Cancel Invite
50
+ </v-btn>
51
+ </template>
52
+ </TableMain>
53
+ </v-col>
54
+
55
+ <!-- DIALOG -->
56
+ <v-dialog v-model="dialog" max-width="500">
57
+ <InvitationForm title="Invite Client" app="organization" @success="handleSuccess" @cancel="dialog = false" />
58
+ </v-dialog>
59
+
60
+ </v-row>
61
+ </template>
62
+ <script setup lang="ts">
63
+ import useOrg from '@7365admin1/layer-common/composables/useOrg'
64
+ import useRole from '@7365admin1/layer-common/composables/useRole'
65
+ import useVerification from '@7365admin1/layer-common/composables/useVerification'
66
+ import { computed, onMounted, ref, watch } from 'vue'
67
+
68
+ const tab = ref<'active' | 'suspended' | 'pending'>('active')
69
+ const dialog = ref(false)
70
+
71
+ const { getAll } = useOrg()
72
+ const { getVerifications, cancelUserInvitation } = useVerification()
73
+ const { getRoleById } = useRole()
74
+ const roleCache = ref<Record<string, string>>({})
75
+
76
+ /* ================= STATE ================= */
77
+ const items = ref<any[]>([])
78
+ const loading = ref(false)
79
+
80
+ const page = ref(1)
81
+ const pages = ref(1)
82
+ const search = ref("")
83
+ async function resolveRoleName(id: string) {
84
+ if (!id) return "-"
85
+
86
+ if (roleCache.value[id]) return roleCache.value[id]
87
+
88
+ const res = await getRoleById(id)
89
+
90
+ const roleName = res?.name ?? "-"
91
+
92
+ roleCache.value[id] = roleName
93
+
94
+ return roleName
95
+ }
96
+ /* ================= HEADERS ================= */
97
+ const orgHeaders = [
98
+ { title: "Organization Name", key: "name" },
99
+ { title: "Site", key: "sites" },
100
+ { title: "Plan Type", key: "plan" },
101
+ { title: "Billing Cycle", key: "bill" },
102
+ { title: "Subscription Start", key: "start" },
103
+ { title: "Subscription End", key: "end" },
104
+ { title: "Status", key: "status" },
105
+ { title: "Action", key: "actions" },
106
+ ]
107
+
108
+ const inviteHeaders = [
109
+ { title: "Email", key: "email" },
110
+ { title: "App", key: "app" },
111
+ { title: "Role", key: "role" },
112
+ { title: "Status", key: "status" },
113
+ { title: "Action", key: "action" },
114
+ ]
115
+
116
+ /* ================= COMPUTED ================= */
117
+ const currentHeaders = computed(() =>
118
+ tab.value === 'pending' ? inviteHeaders : orgHeaders
119
+ )
120
+
121
+ const currentItems = computed(() => items.value)
122
+ const currentPage = computed(() => page.value)
123
+ const currentPages = computed(() => pages.value)
124
+
125
+ const loadingState = computed(() => loading.value)
126
+ const totalItems = ref(0)
127
+ /* ================= API ================= */
128
+ async function fetchData() {
129
+ loading.value = true
130
+
131
+ try {
132
+ if (tab.value === 'pending') {
133
+ const res = await getVerifications({
134
+ status: "pending",
135
+ type: "user-invite",
136
+ page: page.value,
137
+ search: search.value,
138
+ app: "organization",
139
+ })
140
+
141
+ items.value = await Promise.all(
142
+ (res?.items || []).map(async (i: any) => ({
143
+ ...i,
144
+ app: i.metadata?.app ?? "-",
145
+ role: await resolveRoleName(i.metadata?.role),
146
+ }))
147
+ )
148
+
149
+ pages.value = res?.pages || 1
150
+ totalItems.value = res?.total || items.value.length
151
+ } else {
152
+ const res = await getAll({
153
+ page: page.value,
154
+ search: search.value,
155
+ nature: tab.value === "active" ? "" : "suspended",
156
+ })
157
+
158
+ items.value = res?.data?.items || res?.items || []
159
+
160
+ pages.value = res?.data?.totalPages || res?.totalPages || 1
161
+ totalItems.value =
162
+ res?.data?.totalItems ||
163
+ res?.totalItems ||
164
+ items.value.length
165
+ }
166
+ } finally {
167
+ loading.value = false
168
+ }
169
+ }
170
+ const pageRange = computed(() => {
171
+ if (!totalItems.value) return "0-0 of 0"
172
+
173
+ const pageSize = 10
174
+
175
+ const start = (page.value - 1) * pageSize + 1
176
+ const end = Math.min(page.value * pageSize, totalItems.value)
177
+
178
+ return `${start}-${end} of ${totalItems.value}`
179
+ })
180
+ /* ================= EVENTS ================= */
181
+ function onTabChange() {
182
+ page.value = 1
183
+ fetchData()
184
+ }
185
+
186
+ function handleUpdatePage(p: number) {
187
+ page.value = p
188
+ fetchData()
189
+ }
190
+
191
+ function handleRefresh() {
192
+ fetchData()
193
+ }
194
+
195
+ function handleSearch() {
196
+ page.value = 1
197
+ fetchData()
198
+ }
199
+
200
+ /* ================= DIALOG ================= */
201
+ function openInviteDialog() {
202
+ dialog.value = true
203
+ }
204
+
205
+ function handleSuccess() {
206
+ dialog.value = false
207
+ fetchData()
208
+ }
209
+
210
+ /* ================= INIT ================= */
211
+ onMounted(() => {
212
+ fetchData()
213
+ })
214
+
215
+ const cancelLoadingId = ref<string | null>(null)
216
+
217
+ async function cancelInvite(item: any) {
218
+ try {
219
+ cancelLoadingId.value = item._id
220
+
221
+ await cancelUserInvitation(item._id)
222
+
223
+ await fetchData()
224
+ } finally {
225
+ cancelLoadingId.value = null
226
+ }
227
+ }
228
+ </script>
@@ -3,7 +3,7 @@
3
3
  <v-list>
4
4
  <v-list-item>
5
5
  <v-list-item-title class="text-h6 text-white">
6
- {{ APP_NAME }}
6
+ {{ props.title || APP_NAME }}
7
7
  </v-list-item-title>
8
8
  </v-list-item>
9
9
  <slot name="action" />
@@ -36,6 +36,7 @@
36
36
  <script setup lang="ts">
37
37
  const props = defineProps({
38
38
  navigationItems: { type: Array<TNavigationItem>, required: true },
39
+ title: { type: String, default: "" }
39
40
  });
40
41
 
41
42
  const { drawer } = useLocal();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.11.32",
5
+ "version": "1.11.33",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {