@goweekdays/layer-common 0.0.1

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 (48) 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 +21 -0
  9. package/CHANGELOG.md +7 -0
  10. package/README.md +73 -0
  11. package/app.vue +3 -0
  12. package/components/BtnUploadFile.vue +139 -0
  13. package/components/Input/Date.vue +177 -0
  14. package/components/Input/Number.vue +102 -0
  15. package/components/Input/Password.vue +35 -0
  16. package/components/InputLabel.vue +18 -0
  17. package/components/Layout/Header.vue +271 -0
  18. package/components/Layout/NavigationDrawer.vue +24 -0
  19. package/components/ListItem.vue +35 -0
  20. package/components/LocalPagination.vue +31 -0
  21. package/components/NavigationItem.vue +59 -0
  22. package/components/PlaceholderComponent.vue +34 -0
  23. package/components/Snackbar.vue +23 -0
  24. package/components/SpecificAttr.vue +57 -0
  25. package/composables/useAddress.ts +107 -0
  26. package/composables/useLocal.ts +53 -0
  27. package/composables/useLocalAuth.ts +117 -0
  28. package/composables/useOrg.ts +73 -0
  29. package/composables/usePaymentMethod.ts +69 -0
  30. package/composables/usePermission.ts +54 -0
  31. package/composables/useRecapPermission.ts +26 -0
  32. package/composables/useRole.ts +73 -0
  33. package/composables/useSubscription.ts +94 -0
  34. package/composables/useUser.ts +95 -0
  35. package/composables/useUtils.ts +150 -0
  36. package/composables/useVerification.ts +18 -0
  37. package/eslint.config.js +3 -0
  38. package/nuxt.config.ts +46 -0
  39. package/package.json +30 -0
  40. package/plugins/API.ts +42 -0
  41. package/plugins/vuetify.ts +44 -0
  42. package/tsconfig.json +3 -0
  43. package/types/address.d.ts +13 -0
  44. package/types/local.d.ts +43 -0
  45. package/types/org.d.ts +12 -0
  46. package/types/permission.d.ts +24 -0
  47. package/types/role.d.ts +11 -0
  48. package/types/subscription.d.ts +9 -0
@@ -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,271 @@
1
+ <template>
2
+ <v-app-bar scroll-behavior="elevate" scroll-threshold="200">
3
+ <div style="width: 264px" class="ml-2">
4
+ <nuxt-link
5
+ class="text-h6 font-weight-medium text-decoration-none APP_NAME"
6
+ :to="{ name: APP_NAME_ROUTE }"
7
+ >
8
+ {{ APP_NAME }}
9
+ </nuxt-link>
10
+ </div>
11
+
12
+ <v-row no-gutters>
13
+ <v-col cols="6">
14
+ <v-text-field
15
+ v-model="search"
16
+ width="100%"
17
+ prepend-inner-icon="mdi-magnify"
18
+ variant="solo-filled"
19
+ flat
20
+ density="comfortable"
21
+ hide-details
22
+ rounded="xl"
23
+ />
24
+ </v-col>
25
+ </v-row>
26
+
27
+ <template #append>
28
+ <v-btn
29
+ icon="mdi-theme-light-dark"
30
+ variant="text"
31
+ class="mx-2"
32
+ @click="toggleTheme"
33
+ />
34
+
35
+ <v-menu offset="10px" :close-on-content-click="false">
36
+ <template #activator="{ props }">
37
+ <v-btn icon="mdi-dots-grid" variant="text" v-bind="props" />
38
+ </template>
39
+
40
+ <v-card
41
+ width="380"
42
+ max-height="400px"
43
+ elevation="2"
44
+ rounded="xl"
45
+ class="pa-4"
46
+ >
47
+ <v-row no-gutters>
48
+ <v-col cols="12">
49
+ <v-card width="100%" variant="tonal" rounded="t-xl b-0">
50
+ <v-row class="pa-4">
51
+ <template v-for="item in defaultApps">
52
+ <v-col cols="4">
53
+ <v-btn
54
+ :prepend-icon="item.icon"
55
+ stacked
56
+ rounded="xl"
57
+ variant="text"
58
+ class="text-capitalize text-subtitle-2"
59
+ @click="redirect(item.link, item.landingPage)"
60
+ >
61
+ {{ item.title }}
62
+ </v-btn>
63
+ </v-col>
64
+ </template>
65
+ </v-row>
66
+ </v-card>
67
+ </v-col>
68
+
69
+ <v-col cols=" 12" class="mt-2">
70
+ <v-card
71
+ width="100%"
72
+ min-height="50px"
73
+ variant="tonal"
74
+ rounded="b-xl"
75
+ >
76
+ <v-card-title class="text-center pb-0"> Apps </v-card-title>
77
+
78
+ <v-row no-gutters class="px-2 pb-2">
79
+ <v-col
80
+ v-for="item in apps"
81
+ :key="item.title"
82
+ cols="4"
83
+ class="pa-2"
84
+ >
85
+ <v-btn
86
+ stacked
87
+ width="100%"
88
+ :prepend-icon="item.icon"
89
+ rounded="xl"
90
+ variant="text"
91
+ class="text-center text-capitalize text-caption"
92
+ @click="redirect(item.link, item.landingPage)"
93
+ >
94
+ {{ item.title }}
95
+ </v-btn>
96
+ </v-col>
97
+ </v-row>
98
+ </v-card>
99
+ </v-col>
100
+ </v-row>
101
+ </v-card>
102
+ </v-menu>
103
+
104
+ <v-menu offset="10px" :close-on-content-click="false">
105
+ <template #activator="{ props }">
106
+ <v-btn fab variant="text" icon v-bind="props" class="mx-2">
107
+ <v-avatar color="surface-variant" size="42">
108
+ <v-img
109
+ v-if="currentUser?.profile"
110
+ :src="profile"
111
+ width="42"
112
+ height="42"
113
+ ></v-img>
114
+
115
+ <span v-else class="text-h5">{{ getNameInitials(name) }}</span>
116
+ </v-avatar>
117
+ </v-btn>
118
+ </template>
119
+
120
+ <v-card
121
+ width="350"
122
+ max-height="600px"
123
+ elevation="2"
124
+ rounded="xl"
125
+ class="pa-4"
126
+ >
127
+ <v-row no-gutters>
128
+ <v-col cols="12">
129
+ <v-row no-gutters justify="center">
130
+ <v-avatar color="surface-variant" size="75">
131
+ <v-img
132
+ v-if="currentUser?.profile"
133
+ :src="profile"
134
+ width="75"
135
+ height="75"
136
+ ></v-img>
137
+
138
+ <span v-else class="text-h5">{{
139
+ getNameInitials(name)
140
+ }}</span>
141
+ </v-avatar>
142
+ </v-row>
143
+ </v-col>
144
+
145
+ <v-col cols="12" class="text-center mt-2 mb-4">
146
+ {{ currentUser?.firstName }} {{ currentUser?.lastName }}
147
+ </v-col>
148
+
149
+ <v-col cols="12">
150
+ <v-btn
151
+ block
152
+ rounded="xl"
153
+ variant="tonal"
154
+ size="x-large"
155
+ @click="logout()"
156
+ class="text-none text-subtitle-1 font-weight-regular"
157
+ >
158
+ Logout
159
+ </v-btn>
160
+ </v-col>
161
+ </v-row>
162
+ </v-card>
163
+ </v-menu>
164
+ </template>
165
+ </v-app-bar>
166
+ </template>
167
+
168
+ <script setup lang="ts">
169
+ import { useTheme } from "vuetify";
170
+
171
+ const search = defineModel("search", { type: String });
172
+
173
+ const { redirect, apps } = useLocal();
174
+
175
+ const {
176
+ APP_MAIN,
177
+ APP_ACCOUNT,
178
+ APP_ORG,
179
+ APP_ADMIN,
180
+ APP_NAME,
181
+ APP_NAME_ROUTE,
182
+ APP_AFFILIATE,
183
+ } = useRuntimeConfig().public;
184
+
185
+ const theme = useTheme();
186
+
187
+ function toggleTheme() {
188
+ theme.global.name.value = theme.global.current.value.dark ? "light" : "dark";
189
+ }
190
+
191
+ const { currentUser } = useLocalAuth();
192
+
193
+ const profile = computed(() => {
194
+ return `/api/public/${currentUser.value?.profile}`;
195
+ });
196
+
197
+ function logout() {
198
+ if (APP_NAME.toLowerCase() !== "main") {
199
+ redirect(`${APP_MAIN}/logout`);
200
+ return;
201
+ }
202
+
203
+ useRouter().push({ name: "logout" });
204
+ }
205
+
206
+ const name = computed(() => {
207
+ let name = "";
208
+ if (currentUser.value?.firstName) {
209
+ name = currentUser.value.firstName;
210
+ }
211
+
212
+ if (currentUser.value?.lastName) {
213
+ name += ` ${currentUser.value.lastName}`;
214
+ }
215
+
216
+ return name;
217
+ });
218
+
219
+ const { getNameInitials } = useUtils();
220
+
221
+ const defaultApps = [
222
+ {
223
+ title: "Account",
224
+ icon: "mdi-account",
225
+ link: APP_ACCOUNT,
226
+ landingPage: "home",
227
+ },
228
+ {
229
+ title: "Organization",
230
+ icon: "mdi-domain",
231
+ link: APP_ORG,
232
+ landingPage: "home",
233
+ },
234
+ {
235
+ title: "Admin",
236
+ icon: "mdi-security",
237
+ link: APP_ADMIN,
238
+ landingPage: "home",
239
+ },
240
+ {
241
+ title: "Affiliate",
242
+ icon: "mdi-account-multiple-check",
243
+ link: APP_AFFILIATE,
244
+ landingPage: "home",
245
+ },
246
+ ];
247
+ </script>
248
+
249
+ <style scoped>
250
+ .APP_NAME {
251
+ display: inline-block;
252
+ word-wrap: break-word;
253
+ white-space: normal;
254
+ text-align: center;
255
+ color: unset !important;
256
+ }
257
+
258
+ .open-list {
259
+ transition: transform 0.2s ease-in-out;
260
+ transform: rotate(180deg);
261
+ }
262
+
263
+ .close-list {
264
+ transition: transform 0.3s ease-in-out;
265
+ transform: rotate(0deg);
266
+ }
267
+
268
+ .v-list-group__items .v-list-item {
269
+ padding-inline-start: 32px !important;
270
+ }
271
+ </style>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <v-navigation-drawer permanent floating class="pr-2">
3
+ <v-list>
4
+ <slot name="action"></slot>
5
+ <template
6
+ v-for="(navigationItem, navigationIndex) in props.navigationItems"
7
+ :key="`${navigationItem.route.name}-${navigationIndex}`"
8
+ >
9
+ <NavigationItem
10
+ :title="navigationItem.title"
11
+ :icon="navigationItem.icon"
12
+ :route="navigationItem.route"
13
+ :children="navigationItem.children"
14
+ />
15
+ </template>
16
+ </v-list>
17
+ </v-navigation-drawer>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ const props = defineProps({
22
+ navigationItems: { type: Array<TNavigationItem>, required: true },
23
+ });
24
+ </script>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <v-list-item :class="props.divider ? border : defaultBorder">
3
+ <v-row>
4
+ <v-col cols="4" class="text-subtitle-2">
5
+ <slot name="title"> List Title </slot>
6
+ </v-col>
7
+
8
+ <v-col cols="8" class="text-subtitle-2">
9
+ <slot name="value"> value </slot>
10
+ </v-col>
11
+ </v-row>
12
+
13
+ <template #append>
14
+ <slot name="append">
15
+ <v-icon size="30">{{ props.icon }}</v-icon>
16
+ </slot>
17
+ </template>
18
+ </v-list-item>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ const props = defineProps({
23
+ divider: {
24
+ type: Boolean,
25
+ default: true,
26
+ },
27
+ icon: {
28
+ type: String,
29
+ default: "mdi-chevron-right",
30
+ },
31
+ });
32
+
33
+ const defaultBorder = "pa-0 pl-8 pr-6 py-4";
34
+ const border = "border-b-sm" + " " + defaultBorder;
35
+ </script>
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <div class="arrow-navigation">
3
+ <v-btn icon="mdi-chevron-left" variant="text" density="comfortable" :disabled="page <= 1" @click="decrement" />
4
+ <v-btn
5
+ icon="mdi-chevron-right" variant="text" density="comfortable" :disabled="page >= props.length"
6
+ @click="increment" />
7
+ </div>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ const page = defineModel({ type: Number, default: 0 });
12
+ function increment() {
13
+ page.value++;
14
+ }
15
+
16
+ function decrement() {
17
+ page.value--;
18
+ }
19
+ const emit = defineEmits(['update:value']);
20
+ watch(page, () => {
21
+ emit('update:value', page.value);
22
+ });
23
+
24
+ const props = defineProps({
25
+ length: {
26
+ type: Number,
27
+ required: true,
28
+ default: 0
29
+ }
30
+ });
31
+ </script>
@@ -0,0 +1,59 @@
1
+ <template>
2
+ <v-list-group v-if="children && children.length">
3
+ <template #activator="{ props }">
4
+ <v-list-item
5
+ v-bind="props"
6
+ :prepend-icon="icon"
7
+ rounded="e-pill"
8
+ class="text-subtitle-2"
9
+ >
10
+ {{ title }}
11
+ </v-list-item>
12
+ </template>
13
+
14
+ <NavigationItem
15
+ v-for="(child, childIndex) in children"
16
+ :key="`${child.title}-${childIndex}`"
17
+ :title="child.title"
18
+ :icon="child.icon"
19
+ :route="child.route"
20
+ :children="child.children"
21
+ />
22
+ </v-list-group>
23
+
24
+ <v-list-item
25
+ v-else
26
+ :prepend-icon="icon"
27
+ :to="props.route"
28
+ rounded="e-pill"
29
+ class="text-subtitle-2"
30
+ >
31
+ {{ title }}
32
+ </v-list-item>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ const props = defineProps({
37
+ title: {
38
+ type: String,
39
+ required: true,
40
+ default: "Title",
41
+ },
42
+ icon: {
43
+ type: String,
44
+ required: false,
45
+ default: "",
46
+ },
47
+ route: {
48
+ type: Object,
49
+ default() {
50
+ return { name: "", params: {} };
51
+ },
52
+ },
53
+ children: {
54
+ type: Array<TNavigationItem>,
55
+ required: false,
56
+ default: () => [],
57
+ },
58
+ });
59
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div class="maintenance-container">
3
+ <h1>🚧 Page Under Maintenance</h1>
4
+ <p>
5
+ We're currently working on improving this page to serve you better. Please
6
+ check back soon!
7
+ </p>
8
+ <p>Thank you for your patience.</p>
9
+ </div>
10
+ </template>
11
+
12
+ <style scoped>
13
+ .maintenance-container {
14
+ display: flex;
15
+ flex-direction: column;
16
+ justify-content: center;
17
+ align-items: center;
18
+ text-align: center;
19
+ min-height: 50vh;
20
+ margin: 0;
21
+ font-family: Arial, sans-serif;
22
+ color: #333;
23
+ }
24
+
25
+ h1 {
26
+ font-size: 2rem;
27
+ margin-bottom: 1rem;
28
+ }
29
+
30
+ p {
31
+ font-size: 1.1rem;
32
+ line-height: 1.5;
33
+ }
34
+ </style>
@@ -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,107 @@
1
+ export default function useAddress() {
2
+ function add({
3
+ type = "",
4
+ user = "",
5
+ org = "",
6
+ country = "",
7
+ countryCode = "",
8
+ phoneNumber = "",
9
+ address = "",
10
+ continuedAddress = "",
11
+ city = "",
12
+ province = "",
13
+ postalCode = "",
14
+ taxId = "",
15
+ } = {}) {
16
+ return useNuxtApp().$api("/api/addresses", {
17
+ method: "POST",
18
+ body: {
19
+ type,
20
+ user,
21
+ org,
22
+ country,
23
+ countryCode,
24
+ phoneNumber,
25
+ address,
26
+ continuedAddress,
27
+ city,
28
+ province,
29
+ postalCode,
30
+ taxId,
31
+ },
32
+ });
33
+ }
34
+
35
+ function getByUserId(user = "") {
36
+ return useNuxtApp().$api<TAddress>(`/api/addresses/user/${user}`);
37
+ }
38
+
39
+ const _address = useState("address", (): TAddress => {
40
+ return {
41
+ type: "",
42
+ user: "",
43
+ org: "",
44
+ country: "",
45
+ address: "",
46
+ continuedAddress: "",
47
+ city: "",
48
+ province: "",
49
+ postalCode: "",
50
+ taxId: "",
51
+ };
52
+ });
53
+
54
+ function reset() {
55
+ _address.value.type = "";
56
+ _address.value.user = "";
57
+ _address.value.org = "";
58
+ _address.value.country = "";
59
+ _address.value.address = "";
60
+ _address.value.continuedAddress = "";
61
+ _address.value.city = "";
62
+ _address.value.province = "";
63
+ _address.value.postalCode = "";
64
+ _address.value.taxId = "";
65
+ }
66
+
67
+ function set({
68
+ type = "",
69
+ user = "",
70
+ org = "",
71
+ country = "",
72
+ address = "",
73
+ continuedAddress = "",
74
+ city = "",
75
+ province = "",
76
+ postalCode = "",
77
+ taxId = "",
78
+ } = {}) {
79
+ _address.value.type = type;
80
+ _address.value.user = user;
81
+ _address.value.org = org;
82
+ _address.value.country = country;
83
+ _address.value.address = address;
84
+ _address.value.continuedAddress = continuedAddress;
85
+ _address.value.city = city;
86
+ _address.value.province = province;
87
+ _address.value.postalCode = postalCode;
88
+ _address.value.taxId = taxId;
89
+ }
90
+
91
+ const { address, city, province, postalCode, country } = _address.value;
92
+
93
+ const completeAddress = computed(() => {
94
+ return `${address ? `${address}, ` : ""}${
95
+ city ? `${city}, ` : ""
96
+ } ${province} ${postalCode ? `${postalCode}, ` : ""} ${country}`;
97
+ });
98
+
99
+ return {
100
+ add,
101
+ reset,
102
+ set,
103
+ getByUserId,
104
+ address: _address,
105
+ completeAddress,
106
+ };
107
+ }