@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,124 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, nextTick, useAttrs } from "vue";
3
+
4
+ const props = defineProps({
5
+ infinityEnabled: { type: Boolean, default: false }, // Enable ∞ display for 0
6
+ });
7
+
8
+ const modelValue = defineModel<number>(); // Store actual number
9
+ const inputRef = ref<HTMLInputElement | null>(null);
10
+ const attrs = useAttrs();
11
+
12
+ let cursorPosition = 0;
13
+ let forceCursorToEnd = true;
14
+
15
+ // Computed property to format value with commas or infinity sign
16
+ const formattedValue = computed({
17
+ get: () => {
18
+ if (props.infinityEnabled && modelValue.value === 0) return "∞"; // Show ∞ if enabled
19
+ return modelValue.value?.toLocaleString() ?? "";
20
+ },
21
+ set: (val: string) => {
22
+ if (props.infinityEnabled && val === "∞") {
23
+ modelValue.value = 0; // Convert back to 0 internally
24
+ return;
25
+ }
26
+
27
+ const rawValue = val.replace(/\D/g, ""); // Remove non-numeric characters
28
+ const numericValue = rawValue ? Number(rawValue) : 0;
29
+
30
+ if (!isNaN(numericValue)) {
31
+ modelValue.value = numericValue;
32
+ nextTick(() => restoreCursor());
33
+ }
34
+ },
35
+ });
36
+
37
+ // Handle keydown for navigation & number changes
38
+ const handleKeyDown = (event: KeyboardEvent) => {
39
+ if (!inputRef.value || modelValue.value === undefined) return;
40
+
41
+ const { selectionStart, selectionEnd, value } = inputRef.value;
42
+ const isAllSelected = selectionStart === 0 && selectionEnd === value.length;
43
+
44
+ if (
45
+ !/^\d$/.test(event.key) &&
46
+ ![
47
+ "Backspace",
48
+ "Delete",
49
+ "ArrowLeft",
50
+ "ArrowRight",
51
+ "ArrowUp",
52
+ "ArrowDown",
53
+ ].includes(event.key)
54
+ ) {
55
+ event.preventDefault();
56
+ return;
57
+ }
58
+
59
+ if (isAllSelected && /^\d$/.test(event.key)) {
60
+ event.preventDefault();
61
+ modelValue.value = Number(event.key);
62
+ forceCursorToEnd = true;
63
+ nextTick(() => restoreCursor());
64
+ return;
65
+ }
66
+
67
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
68
+ forceCursorToEnd = false;
69
+ return;
70
+ }
71
+
72
+ if (event.key === "ArrowUp") {
73
+ event.preventDefault();
74
+ modelValue.value += 1;
75
+ forceCursorToEnd = true;
76
+ } else if (event.key === "ArrowDown") {
77
+ event.preventDefault();
78
+ modelValue.value = Math.max(0, modelValue.value - 1);
79
+ forceCursorToEnd = true;
80
+ } else {
81
+ cursorPosition = selectionStart || 0;
82
+ }
83
+
84
+ nextTick(() => restoreCursor());
85
+ };
86
+
87
+ // Restore cursor position
88
+ const restoreCursor = () => {
89
+ if (!inputRef.value) return;
90
+ const length = formattedValue.value.length;
91
+
92
+ if (forceCursorToEnd) {
93
+ inputRef.value.setSelectionRange(length, length);
94
+ } else {
95
+ inputRef.value.setSelectionRange(cursorPosition, cursorPosition);
96
+ }
97
+ };
98
+ </script>
99
+
100
+ <template>
101
+ <v-text-field
102
+ ref="inputRef"
103
+ v-model="formattedValue"
104
+ v-bind="attrs"
105
+ type="text"
106
+ @keydown="handleKeyDown"
107
+ >
108
+ <template v-if="$slots.prepend" v-slot:prepend>
109
+ <slot name="prepend"></slot>
110
+ </template>
111
+
112
+ <template v-if="$slots.append" v-slot:append>
113
+ <slot name="append"></slot>
114
+ </template>
115
+
116
+ <template v-if="$slots['append-inner']" v-slot:append-inner>
117
+ <slot name="append-inner"></slot>
118
+ </template>
119
+
120
+ <template v-if="$slots['prepend-inner']" v-slot:prepend-inner>
121
+ <slot name="prepend-inner"></slot>
122
+ </template>
123
+ </v-text-field>
124
+ </template>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <v-text-field
3
+ id="_password"
4
+ :model-value="modelValue"
5
+ :type="showPassword ? 'text' : 'password'"
6
+ :append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
7
+ @click:append-inner="togglePasswordVisibility"
8
+ @update:model-value="updatePassword"
9
+ v-bind="$attrs"
10
+ autocomplete="off"
11
+ />
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ // Define the props for the v-model
16
+ defineProps({
17
+ modelValue: {
18
+ type: String,
19
+ },
20
+ });
21
+
22
+ // Define emit for updating v-model
23
+ const emit = defineEmits(["update:modelValue"]);
24
+
25
+ // Control password visibility
26
+ const showPassword = ref(false);
27
+ const togglePasswordVisibility = () => {
28
+ showPassword.value = !showPassword.value;
29
+ };
30
+
31
+ // Handle input update and emit to parent
32
+ const updatePassword = (value: string) => {
33
+ emit("update:modelValue", value);
34
+ };
35
+ </script>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <span class="text-subtitle-2 font-weight-bold">
3
+ {{ title }} <span v-if="props.required" class="text-error">*</span>
4
+ </span>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ const props = defineProps({
9
+ title: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ required: {
14
+ type: Boolean,
15
+ default: false,
16
+ },
17
+ });
18
+ </script>
@@ -0,0 +1,195 @@
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 mr-2"
7
+ rounded="pill"
8
+ variant="tonal"
9
+ :to="props.inviteRoute"
10
+ size="large"
11
+ v-if="props.inviteMember"
12
+ >
13
+ Invite member
14
+ </v-btn>
15
+ </v-row>
16
+ </v-col>
17
+
18
+ <v-col cols="12">
19
+ <v-card
20
+ width="100%"
21
+ variant="outlined"
22
+ border="thin"
23
+ rounded="lg"
24
+ :loading="loading"
25
+ >
26
+ <v-toolbar density="compact" color="grey-lighten-4">
27
+ <template #prepend>
28
+ <v-btn fab icon density="comfortable" @click="getInvitations()">
29
+ <v-icon>mdi-refresh</v-icon>
30
+ </v-btn>
31
+ </template>
32
+
33
+ <template #append>
34
+ <v-row no-gutters justify="end" align="center">
35
+ <span class="mr-2 text-caption text-fontgray">
36
+ {{ pageRange }}
37
+ </span>
38
+ <local-pagination
39
+ v-model="page"
40
+ :length="pages"
41
+ @update:value="getInvitations()"
42
+ />
43
+ </v-row>
44
+ </template>
45
+
46
+ <template #extension>
47
+ <v-tabs>
48
+ <v-tab
49
+ :to="{
50
+ name: 'org-organization-invitations-status-status',
51
+ params: { status: 'pending', organization },
52
+ }"
53
+ >
54
+ Pending
55
+ </v-tab>
56
+ <v-tab
57
+ :to="{
58
+ name: 'org-organization-invitations-status-status',
59
+ params: { status: 'expired', organization },
60
+ }"
61
+ >
62
+ Expired
63
+ </v-tab>
64
+ </v-tabs>
65
+ </template>
66
+ </v-toolbar>
67
+
68
+ <v-data-table
69
+ :headers="headers"
70
+ :items="items"
71
+ item-value="_id"
72
+ items-per-page="20"
73
+ fixed-header
74
+ hide-default-footer
75
+ hide-default-header
76
+ class="table-height"
77
+ :loading="loading"
78
+ @click:row="tableRowClickHandler"
79
+ >
80
+ <template #item.permissions="{ value }">
81
+ <span class="text-caption font-weight-bold text-capitalize">
82
+ permissions
83
+ </span>
84
+ <v-chip>{{ value.length }}</v-chip>
85
+ </template>
86
+ </v-data-table>
87
+ </v-card>
88
+ </v-col>
89
+ </v-row>
90
+ </template>
91
+
92
+ <script setup lang="ts">
93
+ const props = defineProps({
94
+ status: {
95
+ type: String,
96
+ default: "active",
97
+ },
98
+ app: {
99
+ type: String,
100
+ default: "organization",
101
+ },
102
+ inviteMember: {
103
+ type: Boolean,
104
+ default: false,
105
+ },
106
+ inviteRoute: {
107
+ type: Object as PropType<Record<string, any>>,
108
+ default: () => ({
109
+ name: "index",
110
+ params: {},
111
+ }),
112
+ },
113
+ });
114
+
115
+ const organization = (useRoute().params.organization as string) ?? "";
116
+
117
+ const headers = [
118
+ {
119
+ title: "Date",
120
+ value: "createdAt",
121
+ },
122
+ {
123
+ title: "E-mail",
124
+ value: "email",
125
+ },
126
+ {
127
+ title: "App",
128
+ value: "metadata.app",
129
+ },
130
+ ];
131
+
132
+ const { getVerifications } = useVerification();
133
+
134
+ const page = ref(1);
135
+ const pages = ref(0);
136
+ const pageRange = ref("-- - -- of --");
137
+
138
+ const items = ref<Array<Record<string, any>>>([]);
139
+ const { headerSearch } = useLocal();
140
+
141
+ const {
142
+ data: getInviteReq,
143
+ refresh: getInvitations,
144
+ status: getInviteReqStatus,
145
+ } = await useLazyAsyncData("get-invitations", () =>
146
+ getVerifications({
147
+ page: page.value,
148
+ search: headerSearch.value,
149
+ status: props.status,
150
+ type: "user-invite,member-invite",
151
+ app: props.app,
152
+ })
153
+ );
154
+
155
+ const loading = computed(() => getInviteReqStatus.value === "pending");
156
+
157
+ watchEffect(() => {
158
+ if (getInviteReq.value) {
159
+ const _items = getInviteReq.value.items;
160
+ items.value = _items.length
161
+ ? _items.map((i: Record<string, any>) => ({
162
+ ...i,
163
+ createdAt: new Date(i.createdAt).toLocaleString(),
164
+ }))
165
+ : [];
166
+ pages.value = getInviteReq.value.pages;
167
+ pageRange.value = getInviteReq.value.pageRange;
168
+ }
169
+ });
170
+
171
+ function tableRowClickHandler(_: any, row: any) {
172
+ const item = items.value[row.index];
173
+ useRouter().push({
174
+ name: "roles-permissions-id",
175
+ params: { id: item._id },
176
+ });
177
+ }
178
+
179
+ const selected = ref<Array<string>>([]);
180
+ const selectAll = ref(false);
181
+
182
+ watch(selectAll, (curr) => {
183
+ selected.value.splice(0, selected.value.length);
184
+ if (curr) {
185
+ const ids = items.value.map((i) => i._id as string);
186
+ selected.value.push(...ids);
187
+ }
188
+ });
189
+ </script>
190
+
191
+ <style scoped>
192
+ .table-height {
193
+ max-height: calc(100vh - (220px));
194
+ }
195
+ </style>
@@ -0,0 +1,285 @@
1
+ <template>
2
+ <v-app-bar scroll-behavior="elevate" scroll-threshold="200">
3
+ <v-app-bar-nav-icon @click="drawer = !drawer" />
4
+ <div style="width: 206px" class="ml-4">
5
+ <nuxt-link
6
+ class="text-h6 font-weight-medium text-decoration-none APP_NAME"
7
+ :to="{ name: APP_NAME_ROUTE }"
8
+ >
9
+ {{ APP_NAME }}
10
+ </nuxt-link>
11
+ </div>
12
+
13
+ <v-row no-gutters>
14
+ <v-col cols="12" lg="6" md="9">
15
+ <v-text-field
16
+ v-model="_search"
17
+ width="100%"
18
+ prepend-inner-icon="mdi-magnify"
19
+ variant="solo-filled"
20
+ flat
21
+ density="comfortable"
22
+ hide-details
23
+ rounded="xl"
24
+ />
25
+ </v-col>
26
+ </v-row>
27
+
28
+ <template #append>
29
+ <v-btn
30
+ icon="mdi-theme-light-dark"
31
+ variant="text"
32
+ class="mx-2"
33
+ @click="toggleTheme"
34
+ />
35
+
36
+ <v-menu offset="10px" :close-on-content-click="false">
37
+ <template #activator="{ props }">
38
+ <v-btn icon="mdi-dots-grid" variant="text" v-bind="props" />
39
+ </template>
40
+
41
+ <v-card
42
+ width="380"
43
+ max-height="400px"
44
+ elevation="2"
45
+ rounded="xl"
46
+ class="pa-4"
47
+ >
48
+ <v-row no-gutters>
49
+ <v-col cols="12">
50
+ <v-card width="100%" variant="tonal" rounded="t-xl b-0">
51
+ <v-row class="pa-4">
52
+ <template v-for="item in defaultApps">
53
+ <v-col cols="4">
54
+ <v-btn
55
+ :prepend-icon="item.icon"
56
+ stacked
57
+ rounded="xl"
58
+ variant="text"
59
+ class="text-capitalize text-subtitle-2"
60
+ @click="redirect(item.link, item.landingPage)"
61
+ >
62
+ {{ item.title }}
63
+ </v-btn>
64
+ </v-col>
65
+ </template>
66
+ </v-row>
67
+ </v-card>
68
+ </v-col>
69
+
70
+ <v-col cols=" 12" class="mt-2">
71
+ <v-card
72
+ width="100%"
73
+ min-height="50px"
74
+ variant="tonal"
75
+ rounded="b-xl"
76
+ >
77
+ <v-card-title class="text-center pb-0"> Apps </v-card-title>
78
+
79
+ <v-row no-gutters class="px-2 pb-2">
80
+ <v-col
81
+ v-for="item in apps"
82
+ :key="item.title"
83
+ cols="4"
84
+ class="pa-2"
85
+ >
86
+ <v-btn
87
+ stacked
88
+ width="100%"
89
+ :prepend-icon="item.icon"
90
+ rounded="xl"
91
+ variant="text"
92
+ class="text-center text-capitalize text-caption"
93
+ @click="redirect(item.link, item.landingPage)"
94
+ >
95
+ {{ item.title }}
96
+ </v-btn>
97
+ </v-col>
98
+ </v-row>
99
+ </v-card>
100
+ </v-col>
101
+ </v-row>
102
+ </v-card>
103
+ </v-menu>
104
+
105
+ <v-menu offset="10px" :close-on-content-click="false">
106
+ <template #activator="{ props }">
107
+ <v-btn fab variant="text" icon v-bind="props" class="mx-2">
108
+ <v-avatar color="surface-variant" size="42">
109
+ <v-img
110
+ v-if="currentUser?.profile"
111
+ :src="profile"
112
+ width="42"
113
+ height="42"
114
+ ></v-img>
115
+
116
+ <span v-else class="text-subtitle-1">{{
117
+ getNameInitials(name)
118
+ }}</span>
119
+ </v-avatar>
120
+ </v-btn>
121
+ </template>
122
+
123
+ <v-card
124
+ width="350"
125
+ max-height="600px"
126
+ elevation="2"
127
+ rounded="xl"
128
+ class="pa-4"
129
+ >
130
+ <v-row no-gutters>
131
+ <v-col cols="12">
132
+ <v-row no-gutters justify="center">
133
+ <v-avatar color="surface-variant" size="75">
134
+ <v-img
135
+ v-if="currentUser?.profile"
136
+ :src="profile"
137
+ width="75"
138
+ height="75"
139
+ ></v-img>
140
+
141
+ <span v-else class="text-h5">{{
142
+ getNameInitials(name)
143
+ }}</span>
144
+ </v-avatar>
145
+ </v-row>
146
+ </v-col>
147
+
148
+ <v-col cols="12" class="text-center mt-2 mb-4">
149
+ {{ currentUser?.firstName }} {{ currentUser?.lastName }}
150
+ </v-col>
151
+
152
+ <v-col cols="12">
153
+ <v-btn
154
+ block
155
+ rounded="xl"
156
+ variant="tonal"
157
+ size="x-large"
158
+ @click="logout()"
159
+ class="text-none text-subtitle-1 font-weight-regular"
160
+ >
161
+ Logout
162
+ </v-btn>
163
+ </v-col>
164
+ </v-row>
165
+ </v-card>
166
+ </v-menu>
167
+ </template>
168
+ </v-app-bar>
169
+ </template>
170
+
171
+ <script setup lang="ts">
172
+ import { useTheme } from "vuetify";
173
+
174
+ const { getNameInitials, debounce } = useUtils();
175
+
176
+ const _search = ref("");
177
+
178
+ const search = defineModel("search", { type: String });
179
+
180
+ watch(
181
+ _search,
182
+ debounce((value) => {
183
+ search.value = value;
184
+ }, 1000)
185
+ );
186
+
187
+ const { redirect, apps, drawer } = useLocal();
188
+
189
+ const {
190
+ APP_MAIN,
191
+ APP_ACCOUNT,
192
+ APP_ORG,
193
+ APP_ADMIN,
194
+ APP_NAME,
195
+ APP_NAME_ROUTE,
196
+ APP_AFFILIATE,
197
+ } = useRuntimeConfig().public;
198
+
199
+ const theme = useTheme();
200
+
201
+ function toggleTheme() {
202
+ theme.global.name.value = theme.global.current.value.dark ? "light" : "dark";
203
+ }
204
+
205
+ const { currentUser } = useLocalAuth();
206
+
207
+ const profile = computed(() => {
208
+ return `/api/public/${currentUser.value?.profile}`;
209
+ });
210
+
211
+ function logout() {
212
+ if (APP_NAME.toLowerCase() !== "main") {
213
+ redirect(`${APP_MAIN}/logout`);
214
+ return;
215
+ }
216
+
217
+ useRouter().push({ name: "logout" });
218
+ }
219
+
220
+ const name = computed(() => {
221
+ let name = "";
222
+ if (currentUser.value?.firstName) {
223
+ name = currentUser.value.firstName;
224
+ }
225
+
226
+ if (currentUser.value?.lastName) {
227
+ name += ` ${currentUser.value.lastName}`;
228
+ }
229
+
230
+ return name;
231
+ });
232
+
233
+ const defaultApps = computed(() => [
234
+ {
235
+ title: "Account",
236
+ icon: "mdi-account",
237
+ link: APP_ACCOUNT,
238
+ landingPage: "home",
239
+ },
240
+ {
241
+ title: "Business",
242
+ icon: "mdi-domain",
243
+ link: APP_ORG,
244
+ landingPage: currentUser.value?.defaultOrg
245
+ ? `org/${currentUser.value?.defaultOrg}`
246
+ : "",
247
+ },
248
+ {
249
+ title: "Admin",
250
+ icon: "mdi-security",
251
+ link: APP_ADMIN,
252
+ landingPage: "home",
253
+ },
254
+ {
255
+ title: "Affiliate",
256
+ icon: "mdi-account-multiple-check",
257
+ link: APP_AFFILIATE,
258
+ landingPage: "home",
259
+ },
260
+ ]);
261
+ </script>
262
+
263
+ <style scoped>
264
+ .APP_NAME {
265
+ display: inline-block;
266
+ word-wrap: break-word;
267
+ white-space: normal;
268
+ text-align: center;
269
+ color: unset !important;
270
+ }
271
+
272
+ .open-list {
273
+ transition: transform 0.2s ease-in-out;
274
+ transform: rotate(180deg);
275
+ }
276
+
277
+ .close-list {
278
+ transition: transform 0.3s ease-in-out;
279
+ transform: rotate(0deg);
280
+ }
281
+
282
+ .v-list-group__items .v-list-item {
283
+ padding-inline-start: 32px !important;
284
+ }
285
+ </style>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <v-navigation-drawer
3
+ v-model="drawer"
4
+ permanent
5
+ floating
6
+ :class="props.class"
7
+ :location="props.location"
8
+ >
9
+ <slot name="action"></slot>
10
+ <v-list>
11
+ <template
12
+ v-for="(navigationItem, navigationIndex) in props.navigationItems"
13
+ :key="`${navigationItem.title}-${navigationIndex}`"
14
+ >
15
+ <NavigationItem
16
+ :title="navigationItem.title"
17
+ :icon="navigationItem.icon"
18
+ :route="navigationItem.route"
19
+ :children="navigationItem.children"
20
+ :disabled="navigationItem.disabled"
21
+ :rounded="props.rounded"
22
+ />
23
+ </template>
24
+ </v-list>
25
+
26
+ <template #append>
27
+ <slot name="append"></slot>
28
+ </template>
29
+ </v-navigation-drawer>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import type { PropType } from "vue";
34
+
35
+ const props = defineProps({
36
+ navigationItems: { type: Array<TNavigationItem>, required: true },
37
+ rounded: {
38
+ type: String,
39
+ default: "e-pill",
40
+ },
41
+ class: {
42
+ type: String,
43
+ default: "pr-2",
44
+ },
45
+ location: {
46
+ type: String as PropType<"left" | "right">,
47
+ default: "left",
48
+ },
49
+ });
50
+
51
+ const { drawer } = useLocal();
52
+ </script>