@eeplatform/nuxt-layer-common 1.0.0

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.
Files changed (108) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.editorconfig +12 -0
  4. package/.github/workflows/main.yml +17 -0
  5. package/.github/workflows/publish.yml +39 -0
  6. package/.nuxtrc +1 -0
  7. package/.playground/app.vue +37 -0
  8. package/.playground/nuxt.config.ts +20 -0
  9. package/CHANGELOG.md +7 -0
  10. package/README.md +73 -0
  11. package/app.vue +3 -0
  12. package/components/AddPaymentMethod.vue +585 -0
  13. package/components/BtnUploadFile.vue +139 -0
  14. package/components/ConfirmDialog.vue +66 -0
  15. package/components/Container/Standard.vue +33 -0
  16. package/components/Input/Date.vue +177 -0
  17. package/components/Input/ListGroupSelection.vue +93 -0
  18. package/components/Input/NewDate.vue +123 -0
  19. package/components/Input/Number.vue +124 -0
  20. package/components/Input/Password.vue +35 -0
  21. package/components/InputLabel.vue +18 -0
  22. package/components/InvitationMain.vue +195 -0
  23. package/components/Layout/Header.vue +285 -0
  24. package/components/Layout/NavigationDrawer.vue +52 -0
  25. package/components/LinkHome.vue +9 -0
  26. package/components/ListItem.vue +35 -0
  27. package/components/LocalPagination.vue +41 -0
  28. package/components/MemberMain.vue +452 -0
  29. package/components/NavigationItem.vue +73 -0
  30. package/components/PlaceholderComponent.vue +34 -0
  31. package/components/RolePermissionFormCreate.vue +179 -0
  32. package/components/RolePermissionFormPreviewUpdate.vue +184 -0
  33. package/components/RolePermissionMain.vue +376 -0
  34. package/components/Snackbar.vue +23 -0
  35. package/components/SpecificAttr.vue +57 -0
  36. package/components/Std/Pagination.vue +52 -0
  37. package/components/SwitchContext.vue +109 -0
  38. package/components/SwitchOrg.vue +159 -0
  39. package/components/TableList.vue +130 -0
  40. package/composables/useAddress.ts +144 -0
  41. package/composables/useChartOfAccount.ts +62 -0
  42. package/composables/useCommonPermission.ts +130 -0
  43. package/composables/useFile.ts +29 -0
  44. package/composables/useInvoice.ts +42 -0
  45. package/composables/useLocal.ts +63 -0
  46. package/composables/useLocalAuth.ts +157 -0
  47. package/composables/useLocalSetup.ts +46 -0
  48. package/composables/useMember.ts +107 -0
  49. package/composables/useOrder.ts +22 -0
  50. package/composables/useOrg.ts +106 -0
  51. package/composables/useOrgPermission.ts +27 -0
  52. package/composables/usePayment.ts +22 -0
  53. package/composables/usePaymentMethod.ts +347 -0
  54. package/composables/usePermission.ts +54 -0
  55. package/composables/usePrice.ts +15 -0
  56. package/composables/usePromoCode.ts +43 -0
  57. package/composables/useRecapPermission.ts +26 -0
  58. package/composables/useRole.ts +89 -0
  59. package/composables/useSchoolPermission.ts +13 -0
  60. package/composables/useSubscription.ts +264 -0
  61. package/composables/useUser.ts +102 -0
  62. package/composables/useUtils.ts +294 -0
  63. package/composables/useVerification.ts +19 -0
  64. package/error.vue +41 -0
  65. package/eslint.config.js +3 -0
  66. package/layouts/plain.vue +7 -0
  67. package/middleware/01.auth.ts +14 -0
  68. package/middleware/org.ts +16 -0
  69. package/nuxt.config.ts +48 -0
  70. package/package.json +35 -0
  71. package/pages/index.vue +3 -0
  72. package/pages/payment-method-cancel-link.vue +31 -0
  73. package/pages/payment-method-failed-link.vue +31 -0
  74. package/pages/payment-method-linked.vue +31 -0
  75. package/pages/require-organization-membership.vue +47 -0
  76. package/pages/unauthorized.vue +29 -0
  77. package/plugins/API.ts +58 -0
  78. package/plugins/iconify.client.ts +5 -0
  79. package/plugins/vuetify.ts +55 -0
  80. package/public/bdo-logo.svg +4 -0
  81. package/public/bpi-logo.svg +74 -0
  82. package/public/chinabank-logo.svg +120 -0
  83. package/public/gcash-logo.png +0 -0
  84. package/public/gcash-logo.svg +65 -0
  85. package/public/grabpay-logo.svg +99 -0
  86. package/public/paymaya-logo.jpg +0 -0
  87. package/public/paymaya-logo.png +0 -0
  88. package/public/paymaya-logo.svg +25 -0
  89. package/public/qrph-c567ff0f-ab6d-4662-86bf-24c6c731d8a8-logo.svg +20 -0
  90. package/public/rcbc-logo.svg +15 -0
  91. package/public/shopeepay-logo.svg +89 -0
  92. package/public/ubp-logo.svg +88 -0
  93. package/tsconfig.json +3 -0
  94. package/types/address.d.ts +13 -0
  95. package/types/invoice.d.ts +28 -0
  96. package/types/local.d.ts +25 -0
  97. package/types/member.d.ts +12 -0
  98. package/types/org.d.ts +13 -0
  99. package/types/payment-method.d.ts +11 -0
  100. package/types/payment.d.ts +18 -0
  101. package/types/permission.d.ts +14 -0
  102. package/types/price.d.ts +17 -0
  103. package/types/promo-code.d.ts +19 -0
  104. package/types/role.d.ts +13 -0
  105. package/types/subscription.d.ts +29 -0
  106. package/types/user.d.ts +21 -0
  107. package/types/verification.d.ts +15 -0
  108. package/types/xendit.d.ts +3 -0
@@ -0,0 +1,376 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <v-col cols="12" class="mb-2">
4
+ <v-row no-gutters>
5
+ <v-btn
6
+ class="text-none"
7
+ rounded="pill"
8
+ variant="tonal"
9
+ @click="createDialog = true"
10
+ size="large"
11
+ v-if="canCreateRole"
12
+ >
13
+ Create role
14
+ </v-btn>
15
+ </v-row>
16
+ </v-col>
17
+ <v-col cols="12">
18
+ <v-card
19
+ width="100%"
20
+ variant="outlined"
21
+ border="thin"
22
+ rounded="lg"
23
+ :loading="loading"
24
+ >
25
+ <v-toolbar density="compact" color="grey-lighten-4">
26
+ <template #prepend>
27
+ <v-btn fab icon density="comfortable" @click="getRoles()">
28
+ <v-icon>mdi-refresh</v-icon>
29
+ </v-btn>
30
+ </template>
31
+
32
+ <template #append>
33
+ <v-row no-gutters justify="end" align="center">
34
+ <span class="mr-2 text-caption text-fontgray">
35
+ {{ pageRange }}
36
+ </span>
37
+ <local-pagination
38
+ v-model="page"
39
+ :length="pages"
40
+ @update:value="getRoles()"
41
+ />
42
+ </v-row>
43
+ </template>
44
+ </v-toolbar>
45
+
46
+ <v-data-table
47
+ :headers="props.headers"
48
+ :items="items"
49
+ item-value="_id"
50
+ items-per-page="20"
51
+ fixed-header
52
+ hide-default-footer
53
+ hide-default-header
54
+ @click:row="tableRowClickHandler"
55
+ style="max-height: calc(100vh - (180px))"
56
+ >
57
+ <template #item.permissions="{ value }">
58
+ <span class="text-caption font-weight-bold text-capitalize">
59
+ permissions
60
+ </span>
61
+ <v-chip>{{ value.length }}</v-chip>
62
+ </template>
63
+
64
+ <template #item.action-table="{ item }" v-if="canDeleteRole">
65
+ <v-menu :close-on-content-click="false" offset-y width="150">
66
+ <template v-slot:activator="{ props }">
67
+ <v-icon v-bind="props">mdi-dots-horizontal</v-icon>
68
+ </template>
69
+ <v-list>
70
+ <v-list-item @click="openDeleteDialog(item._id)">
71
+ Delete Role
72
+ </v-list-item>
73
+ </v-list>
74
+ </v-menu>
75
+ </template>
76
+ </v-data-table>
77
+ </v-card>
78
+ </v-col>
79
+
80
+ <!-- dialogs -->
81
+ <v-dialog v-model="createDialog" width="500" persistent>
82
+ <RolePermissionFormCreate
83
+ @cancel="createDialog = false"
84
+ :permissions="props.permissions"
85
+ :org="props.orgId"
86
+ @success="success()"
87
+ v-model:type="type"
88
+ :types="props.types"
89
+ @success:create-more="getRoles()"
90
+ />
91
+ </v-dialog>
92
+
93
+ <v-dialog v-model="previewDialog" width="500" persistent>
94
+ <RolePermissionFormPreviewUpdate
95
+ @cancel="previewDialog = false"
96
+ @success="successfulUpdate()"
97
+ :permissions="Permissions"
98
+ :original-permissions="originalPermissions"
99
+ v-model="selectedPermissions"
100
+ v-model:edit="edit"
101
+ :name="name"
102
+ :id="roleId"
103
+ />
104
+ </v-dialog>
105
+
106
+ <ConfirmDialog
107
+ v-model="confirmDialog"
108
+ :loading="deleteLoading"
109
+ @submit="handleDeleteRole"
110
+ >
111
+ <template #title>
112
+ <span class="font-weight-medium text-h5">Delete Role</span>
113
+ </template>
114
+
115
+ <template #description>
116
+ <p class="text-subtitle-2">
117
+ Are you sure you want to delete this role? This action cannot be
118
+ undone.
119
+ </p>
120
+ </template>
121
+
122
+ <template #footer>
123
+ <v-btn
124
+ variant="text"
125
+ @click="confirmDialog = false"
126
+ :disabled="deleteLoading"
127
+ >
128
+ Close
129
+ </v-btn>
130
+ <v-btn
131
+ color="primary"
132
+ variant="flat"
133
+ @click="handleDeleteRole"
134
+ :loading="deleteLoading"
135
+ >
136
+ Delete Role
137
+ </v-btn>
138
+ </template>
139
+ </ConfirmDialog>
140
+
141
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
142
+ </v-row>
143
+ </template>
144
+
145
+ <script setup lang="ts">
146
+ const props = defineProps({
147
+ orgId: {
148
+ type: String,
149
+ default: "",
150
+ },
151
+ permissions: {
152
+ type: Object,
153
+ required: true,
154
+ default: () => ({}),
155
+ },
156
+ types: {
157
+ type: Array as PropType<Array<{ title: string; value: string }>>,
158
+ default: () => [],
159
+ },
160
+ headers: {
161
+ type: Array as PropType<Array<Record<string, any>>>,
162
+ default: () => [
163
+ {
164
+ title: "name",
165
+ value: "name",
166
+ },
167
+ {
168
+ title: "permissions",
169
+ value: "permissions",
170
+ },
171
+ { title: "App", value: "type" },
172
+ { title: "Action", value: "action-table" },
173
+ ],
174
+ },
175
+ canCreateRole: {
176
+ type: Boolean,
177
+ default: false,
178
+ },
179
+ canViewRole: {
180
+ type: Boolean,
181
+ default: false,
182
+ },
183
+ canViewByRole: {
184
+ type: Boolean,
185
+ default: false,
186
+ },
187
+ canDeleteRole: {
188
+ type: Boolean,
189
+ default: false,
190
+ },
191
+ canUpdateRole: {
192
+ type: Boolean,
193
+ default: false,
194
+ },
195
+ });
196
+
197
+ const type = defineModel<string>("type", {
198
+ type: String,
199
+ default: "",
200
+ });
201
+
202
+ const { headerSearch } = useLocal();
203
+
204
+ const { getRoles: _getRoles } = useRole();
205
+
206
+ const page = ref(1);
207
+ const pages = ref(0);
208
+ const pageRange = ref("-- - -- of --");
209
+
210
+ const message = ref("");
211
+ const messageSnackbar = ref(false);
212
+ const messageColor = ref("");
213
+
214
+ const items = ref<Array<Record<string, any>>>([]);
215
+
216
+ const {
217
+ data: getRoleReq,
218
+ refresh: getRoles,
219
+ status: getRoleReqStatus,
220
+ } = useLazyAsyncData(
221
+ "roles-permissions-get-all",
222
+ () =>
223
+ _getRoles({
224
+ page: page.value,
225
+ search: headerSearch.value,
226
+ type: type.value,
227
+ org: props.orgId,
228
+ }),
229
+ {
230
+ watch: [page, headerSearch],
231
+ }
232
+ );
233
+
234
+ const loading = computed(() => getRoleReqStatus.value === "pending");
235
+
236
+ watchEffect(() => {
237
+ if (getRoleReq.value) {
238
+ items.value = getRoleReq.value.items;
239
+ pages.value = getRoleReq.value.pages;
240
+ pageRange.value = getRoleReq.value.pageRange;
241
+ }
242
+ });
243
+
244
+ function tableRowClickHandler(_: any, data: any) {
245
+ previewDialog.value = true;
246
+ roleId.value = data.item._id;
247
+ }
248
+
249
+ const createDialog = ref(false);
250
+
251
+ function success() {
252
+ createDialog.value = false;
253
+ type.value = "";
254
+ getRoles();
255
+ }
256
+
257
+ const previewDialog = ref(false);
258
+
259
+ const name = ref("");
260
+ const selectedPermissions = ref<Array<string>>([]);
261
+ const originalPermissions = ref<Array<string>>([]);
262
+ const edit = ref(false);
263
+
264
+ watchEffect(() => {
265
+ if (!edit.value) {
266
+ selectedPermissions.value = originalPermissions.value;
267
+ }
268
+ });
269
+
270
+ const roleId = ref("");
271
+
272
+ const { getRoleById: _getRoleById, deleteRole } = useRole();
273
+
274
+ const { data: role, refresh: getRoleById } = useLazyAsyncData(
275
+ "role-permissions-get-by-id",
276
+ () => _getRoleById(roleId.value)
277
+ );
278
+
279
+ watchEffect(() => {
280
+ if (roleId.value) {
281
+ getRoleById();
282
+ }
283
+ });
284
+
285
+ watchEffect(() => {
286
+ if (role.value) {
287
+ name.value = role.value.name;
288
+
289
+ selectedPermissions.value = role.value.permissions;
290
+ originalPermissions.value = role.value.permissions;
291
+ }
292
+ });
293
+
294
+ function filterPermissions(
295
+ allPermissions: TPermissions,
296
+ storedPermissions: string[]
297
+ ) {
298
+ const filteredPermissions: TPermissions = {};
299
+
300
+ Object.entries(allPermissions).forEach(([resource, actions]) => {
301
+ const filteredActions = Object.entries(actions)
302
+ .filter(([action]) => storedPermissions.includes(`${resource}:${action}`))
303
+ .reduce((acc: Record<string, any>, [action, data]) => {
304
+ acc[action] = data;
305
+ return acc;
306
+ }, {});
307
+
308
+ if (Object.keys(filteredActions).length > 0) {
309
+ filteredPermissions[resource] = filteredActions;
310
+ }
311
+ });
312
+
313
+ return filteredPermissions;
314
+ }
315
+
316
+ const { permissions: orgPermissions } = useOrgPermission();
317
+ const { permissions: schoolPermissions } = useSchoolPermission();
318
+
319
+ const permissions = computed(() => {
320
+ if (role.value?.type === "organization") {
321
+ return orgPermissions;
322
+ }
323
+
324
+ if (role.value?.type === "school") {
325
+ return schoolPermissions;
326
+ }
327
+
328
+ return {};
329
+ });
330
+
331
+ const Permissions = computed(() => {
332
+ return edit.value
333
+ ? permissions.value
334
+ : filterPermissions(permissions.value, selectedPermissions.value);
335
+ });
336
+
337
+ function successfulUpdate() {
338
+ previewDialog.value = false;
339
+ getRoles();
340
+ getRoleById();
341
+ edit.value = false;
342
+ }
343
+
344
+ const confirmDialog = ref(false);
345
+ const selectedRoleId = ref<string | null>(null);
346
+ const deleteLoading = ref(false);
347
+
348
+ function openDeleteDialog(id: string) {
349
+ selectedRoleId.value = id;
350
+ confirmDialog.value = true;
351
+ }
352
+
353
+ function showMessage(msg: string, color: string) {
354
+ message.value = msg;
355
+ messageColor.value = color;
356
+ messageSnackbar.value = true;
357
+ }
358
+
359
+ async function handleDeleteRole() {
360
+ if (!selectedRoleId.value) return;
361
+ deleteLoading.value = true;
362
+ try {
363
+ const res = await deleteRole(selectedRoleId.value);
364
+
365
+ confirmDialog.value = false;
366
+ showMessage(res.message, "success");
367
+ getRoles();
368
+ } catch (error: any) {
369
+ const errorMessage = error?.response?._data?.message;
370
+ showMessage(errorMessage, "error");
371
+ } finally {
372
+ deleteLoading.value = false;
373
+ selectedRoleId.value = null;
374
+ }
375
+ }
376
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <v-snackbar v-model="snackbar" :color="props.color">
3
+ {{ props.text }}
4
+
5
+ <template v-slot:actions>
6
+ <v-btn variant="text" @click="snackbar = false"> Close </v-btn>
7
+ </template>
8
+ </v-snackbar>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ const snackbar = defineModel({ type: Boolean });
13
+ const props = defineProps({
14
+ text: {
15
+ type: String,
16
+ default: "",
17
+ },
18
+ color: {
19
+ type: String,
20
+ default: "primary",
21
+ },
22
+ });
23
+ </script>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <v-row no-gutters class="pa-4" justify="center">
3
+ <v-col cols="12" :lg="props.lg" :md="props.md" :sm="props.sm">
4
+ <v-row no-gutters>
5
+ <v-col cols="12">
6
+ <v-row no-gutters class="fill-height" align="center">
7
+ <v-btn
8
+ fab
9
+ icon
10
+ variant="text"
11
+ @click="emit('click:back')"
12
+ class="mr-3"
13
+ >
14
+ <v-icon>mdi-arrow-left</v-icon>
15
+ </v-btn>
16
+
17
+ <span class="text-h5">{{ props.title }}</span>
18
+ </v-row>
19
+ </v-col>
20
+
21
+ <v-col cols="12" class="mt-6">
22
+ <v-card variant="outlined" border="thin">
23
+ <slot>
24
+ <PlaceholderComponent />
25
+ </slot>
26
+ </v-card>
27
+ </v-col>
28
+ </v-row>
29
+ </v-col>
30
+ </v-row>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ const emit = defineEmits(["click:back"]);
35
+ const props = defineProps({
36
+ title: {
37
+ type: String,
38
+ default: "Title",
39
+ },
40
+ lg: {
41
+ type: String,
42
+ default: "4",
43
+ },
44
+ md: {
45
+ type: String,
46
+ default: "6",
47
+ },
48
+ sm: {
49
+ type: String,
50
+ default: "6",
51
+ },
52
+ fluid: {
53
+ type: Boolean,
54
+ default: false,
55
+ },
56
+ });
57
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <v-row no-gutters justify="end" align="center">
3
+ <span class="mr-2 text-caption text-fontgray">
4
+ {{ props.pageRange }}
5
+ </span>
6
+ <div class="arrow-navigation">
7
+ <v-btn
8
+ icon="mdi-chevron-left"
9
+ variant="text"
10
+ density="comfortable"
11
+ :disabled="page <= 1"
12
+ @click="decrement"
13
+ />
14
+ <v-btn
15
+ icon="mdi-chevron-right"
16
+ variant="text"
17
+ density="comfortable"
18
+ :disabled="page >= props.pages"
19
+ @click="increment"
20
+ />
21
+ </div>
22
+ </v-row>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ const page = defineModel({ type: Number, default: 0 });
27
+
28
+ const props = defineProps({
29
+ pages: {
30
+ type: Number,
31
+ required: true,
32
+ default: 0,
33
+ },
34
+ pageRange: {
35
+ type: String,
36
+ required: true,
37
+ default: "-- - -- of --",
38
+ },
39
+ });
40
+
41
+ const emit = defineEmits(["refresh", "update:pagination"]);
42
+
43
+ function increment() {
44
+ page.value++;
45
+ emit("update:pagination", page.value);
46
+ }
47
+
48
+ function decrement() {
49
+ page.value--;
50
+ emit("update:pagination", page.value);
51
+ }
52
+ </script>
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <v-menu
3
+ v-model="menu"
4
+ location="bottom"
5
+ offset="5px"
6
+ :close-on-content-click="false"
7
+ >
8
+ <template #activator="{ props }">
9
+ <v-list class="pa-0">
10
+ <v-list-item
11
+ v-bind="props"
12
+ :key="props.title"
13
+ :prepend-icon="prop.icon"
14
+ append-icon="mdi-chevron-down"
15
+ :to="props.route"
16
+ class="text-subtitle-2 font-weight-bold"
17
+ rounded="e-pill"
18
+ >
19
+ <v-list-item-title class="text-truncate">
20
+ {{ name || `Select ${prop.title}` }}
21
+ </v-list-item-title>
22
+ </v-list-item>
23
+ </v-list>
24
+ </template>
25
+
26
+ <v-card width="300px" rounded="lg" class="pa-4">
27
+ <span class="text-subtitle-2 font-weight-bold">
28
+ Switch {{ prop.title }} context
29
+ </span>
30
+ <v-text-field
31
+ v-model="search"
32
+ @keydown.enter="emit('search')"
33
+ density="compact"
34
+ width="100%"
35
+ hide-details
36
+ class="mb-2"
37
+ >
38
+ <template #prepend-inner>
39
+ <v-icon>mdi-magnify</v-icon>
40
+ </template>
41
+ </v-text-field>
42
+ <v-divider />
43
+ <v-list class="pa-0 my-2" density="compact" max-height="200px">
44
+ <v-list-item
45
+ density="compact"
46
+ class="text-caption font-weight-bold"
47
+ v-for="item in prop.items"
48
+ :key="item.value"
49
+ @click="selectItem(item.value)"
50
+ >
51
+ <v-icon size="16" class="mr-2">
52
+ {{ selected === item.value ? "mdi-check" : "" }}
53
+ </v-icon>
54
+
55
+ {{ item.text }}
56
+ </v-list-item>
57
+ </v-list>
58
+
59
+ <slot name="footer"></slot>
60
+ </v-card>
61
+ </v-menu>
62
+ </template>
63
+
64
+ <script setup lang="ts">
65
+ const prop = defineProps({
66
+ title: {
67
+ type: String,
68
+ required: true,
69
+ default: "app",
70
+ },
71
+ items: {
72
+ type: Array as PropType<Array<Record<string, any>>>,
73
+ required: false,
74
+ default: () => [],
75
+ },
76
+ icon: {
77
+ type: String,
78
+ required: false,
79
+ default: "mdi-domain",
80
+ },
81
+ logo: {
82
+ type: String,
83
+ required: false,
84
+ default: "",
85
+ },
86
+ });
87
+
88
+ const menu = defineModel<boolean>("menu", { required: false, default: false });
89
+ const selected = defineModel<string>("selected", {
90
+ required: false,
91
+ default: "",
92
+ });
93
+ const search = defineModel<string>("search", {
94
+ required: false,
95
+ default: "",
96
+ });
97
+
98
+ const emit = defineEmits(["search", "select"]);
99
+
100
+ const name = computed(() => {
101
+ return prop.items.find((item) => item.value === selected.value)?.text ?? "";
102
+ });
103
+
104
+ const selectItem = (value: string) => {
105
+ selected.value = value;
106
+ menu.value = false;
107
+ emit("select", value);
108
+ };
109
+ </script>