@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.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.editorconfig +12 -0
- package/.github/workflows/main.yml +17 -0
- package/.github/workflows/publish.yml +39 -0
- package/.nuxtrc +1 -0
- package/.playground/app.vue +37 -0
- package/.playground/nuxt.config.ts +21 -0
- package/CHANGELOG.md +7 -0
- package/README.md +73 -0
- package/app.vue +3 -0
- package/components/BtnUploadFile.vue +139 -0
- package/components/Input/Date.vue +177 -0
- package/components/Input/Number.vue +102 -0
- package/components/Input/Password.vue +35 -0
- package/components/InputLabel.vue +18 -0
- package/components/Layout/Header.vue +271 -0
- package/components/Layout/NavigationDrawer.vue +24 -0
- package/components/ListItem.vue +35 -0
- package/components/LocalPagination.vue +31 -0
- package/components/NavigationItem.vue +59 -0
- package/components/PlaceholderComponent.vue +34 -0
- package/components/Snackbar.vue +23 -0
- package/components/SpecificAttr.vue +57 -0
- package/composables/useAddress.ts +107 -0
- package/composables/useLocal.ts +53 -0
- package/composables/useLocalAuth.ts +117 -0
- package/composables/useOrg.ts +73 -0
- package/composables/usePaymentMethod.ts +69 -0
- package/composables/usePermission.ts +54 -0
- package/composables/useRecapPermission.ts +26 -0
- package/composables/useRole.ts +73 -0
- package/composables/useSubscription.ts +94 -0
- package/composables/useUser.ts +95 -0
- package/composables/useUtils.ts +150 -0
- package/composables/useVerification.ts +18 -0
- package/eslint.config.js +3 -0
- package/nuxt.config.ts +46 -0
- package/package.json +30 -0
- package/plugins/API.ts +42 -0
- package/plugins/vuetify.ts +44 -0
- package/tsconfig.json +3 -0
- package/types/address.d.ts +13 -0
- package/types/local.d.ts +43 -0
- package/types/org.d.ts +12 -0
- package/types/permission.d.ts +24 -0
- package/types/role.d.ts +11 -0
- package/types/subscription.d.ts +9 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export default function useUtils() {
|
|
2
|
+
const requiredRule = (v: string) => !!v || "Required";
|
|
3
|
+
|
|
4
|
+
const emailRule = (v: string): true | string => {
|
|
5
|
+
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6
|
+
return regex.test(v) || "Please enter a valid email address";
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const passwordRule = (v: string) =>
|
|
10
|
+
v.length >= 8 || "Password must be at least 8 characters long";
|
|
11
|
+
|
|
12
|
+
const confirmPasswordRule =
|
|
13
|
+
(newPassword: Ref<string>, confirmPassword: Ref<string>) => () =>
|
|
14
|
+
confirmPassword.value === newPassword.value || "Passwords must match";
|
|
15
|
+
|
|
16
|
+
const validateEmail = (email: Ref<string>, emailErrors: Ref<string[]>) => {
|
|
17
|
+
emailErrors.value = [];
|
|
18
|
+
|
|
19
|
+
if (!email.value) {
|
|
20
|
+
emailErrors.value.push("Please input your email");
|
|
21
|
+
} else {
|
|
22
|
+
const result = emailRule(email.value);
|
|
23
|
+
if (result !== true) {
|
|
24
|
+
emailErrors.value.push(result);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const validateWord = (value: string): boolean | string => {
|
|
30
|
+
if (value.trim().length < 3) return "Must be at least 3 characters";
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const validatePassword = (
|
|
35
|
+
password: Ref<string>,
|
|
36
|
+
passwordErrors: Ref<string[]>
|
|
37
|
+
) => {
|
|
38
|
+
passwordErrors.value = [];
|
|
39
|
+
|
|
40
|
+
if (!password.value) {
|
|
41
|
+
passwordErrors.value.push("Please input your password");
|
|
42
|
+
} else if (!passwordRule(password.value)) {
|
|
43
|
+
passwordErrors.value.push("Password must be at least 8 characters long");
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function back() {
|
|
48
|
+
useRouter().back();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function debounce<T extends (...args: any[]) => void>(
|
|
52
|
+
func: T,
|
|
53
|
+
wait: number
|
|
54
|
+
): (...args: Parameters<T>) => void {
|
|
55
|
+
let timeoutId: ReturnType<typeof setTimeout>;
|
|
56
|
+
|
|
57
|
+
return function (...args: Parameters<T>) {
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
timeoutId = setTimeout(() => func(...args), wait);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function insertBetween(arr: any, elementToInsert: any) {
|
|
64
|
+
return arr.flatMap((item: any, index: number, array: any) =>
|
|
65
|
+
index < array.length - 1 ? [item, elementToInsert] : [item]
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getNameInitials(name: string) {
|
|
70
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
71
|
+
return ""; // Default fallback for invalid input
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const initials = name
|
|
75
|
+
.trim()
|
|
76
|
+
.split(/\s+/) // Split by spaces
|
|
77
|
+
.map((word) => word[0]?.toUpperCase()) // Take the first letter of each word and capitalize
|
|
78
|
+
.join(""); // Combine all initials
|
|
79
|
+
|
|
80
|
+
return initials.slice(0, 2); // Limit to 2 characters
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getDimensions(
|
|
84
|
+
file: File
|
|
85
|
+
): Promise<{ width: number; height: number }> {
|
|
86
|
+
const img = new Image();
|
|
87
|
+
img.src = URL.createObjectURL(file);
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
img.onload = () => {
|
|
90
|
+
const width = img.width;
|
|
91
|
+
const height = img.height;
|
|
92
|
+
resolve({ width, height });
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
img.onerror = () => {
|
|
96
|
+
reject(new Error("Failed to read image dimensions"));
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function getCountries() {
|
|
102
|
+
try {
|
|
103
|
+
const countries = await useNuxtApp().$api<Array<Record<string, any>>>(
|
|
104
|
+
"https://restcountries.com/v3.1/all",
|
|
105
|
+
{ method: "GET" }
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const uniqueCountries = new Map();
|
|
109
|
+
|
|
110
|
+
countries.forEach((country) => {
|
|
111
|
+
let suffixes = country.idd?.suffixes?.[0] || "";
|
|
112
|
+
let code = `${country.idd?.root || ""}${suffixes}`;
|
|
113
|
+
let name = country.name?.common || "";
|
|
114
|
+
|
|
115
|
+
if (name && code && !uniqueCountries.has(code)) {
|
|
116
|
+
uniqueCountries.set(code, { index: `${name}-${code}`, name, code });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return Array.from(uniqueCountries.values()).sort((a, b) =>
|
|
121
|
+
a.name.localeCompare(b.name)
|
|
122
|
+
);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(error);
|
|
125
|
+
throw new Error("Failed to fetch countries.");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
130
|
+
minimumFractionDigits: 2,
|
|
131
|
+
maximumFractionDigits: 2,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
requiredRule,
|
|
136
|
+
emailRule,
|
|
137
|
+
passwordRule,
|
|
138
|
+
confirmPasswordRule,
|
|
139
|
+
validateEmail,
|
|
140
|
+
validatePassword,
|
|
141
|
+
back,
|
|
142
|
+
debounce,
|
|
143
|
+
insertBetween,
|
|
144
|
+
getNameInitials,
|
|
145
|
+
getDimensions,
|
|
146
|
+
validateWord,
|
|
147
|
+
getCountries,
|
|
148
|
+
formatter,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default function useUser() {
|
|
2
|
+
function getVerifications({
|
|
3
|
+
status = "pending",
|
|
4
|
+
type = "user-invite",
|
|
5
|
+
search = "",
|
|
6
|
+
page = 1,
|
|
7
|
+
email = "",
|
|
8
|
+
} = {}) {
|
|
9
|
+
return useNuxtApp().$api<TKeyValuePair>("/api/verifications", {
|
|
10
|
+
method: "GET",
|
|
11
|
+
query: { status, search, page, type, email },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
getVerifications,
|
|
17
|
+
};
|
|
18
|
+
}
|
package/eslint.config.js
ADDED
package/nuxt.config.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
2
|
+
import vuetify, { transformAssetUrls } from "vite-plugin-vuetify";
|
|
3
|
+
export default defineNuxtConfig({
|
|
4
|
+
devtools: { enabled: true },
|
|
5
|
+
build: {
|
|
6
|
+
transpile: ["vuetify"],
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
runtimeConfig: {
|
|
10
|
+
public: {
|
|
11
|
+
cookieConfig: {
|
|
12
|
+
domain: (process.env.DOMAIN as string) ?? "localhost",
|
|
13
|
+
secure: true,
|
|
14
|
+
maxAge: 30 * 24 * 60 * 60,
|
|
15
|
+
},
|
|
16
|
+
APP_NAME: (process.env.APP_NAME as string) ?? "App",
|
|
17
|
+
APP_NAME_ROUTE: (process.env.APP_NAME_ROUTE as string) ?? "index",
|
|
18
|
+
S3_BUCKET_ENDPOINT: (process.env.S3_BUCKET_ENDPOINT as string) ?? "",
|
|
19
|
+
APP_MAIN: (process.env.APP_MAIN as string) ?? "",
|
|
20
|
+
APP_ACCOUNT: (process.env.APP_ACCOUNT as string) ?? "",
|
|
21
|
+
APP_ADMIN: (process.env.APP_ADMIN as string) ?? "",
|
|
22
|
+
APP_ORG: (process.env.APP_ORG as string) ?? "",
|
|
23
|
+
APP_AFFILIATE: (process.env.APP_AFFILIATE as string) ?? "",
|
|
24
|
+
APP_INVENTORY: (process.env.APP_INVENTORY as string) ?? "",
|
|
25
|
+
APP_ASSET: (process.env.APP_ASSET as string) ?? "",
|
|
26
|
+
APP_BOOK_KEEPING: (process.env.APP_BOOK_KEEPING as string) ?? "",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
modules: [
|
|
31
|
+
(_options, nuxt) => {
|
|
32
|
+
nuxt.hooks.hook("vite:extendConfig", (config) => {
|
|
33
|
+
// @ts-expect-error
|
|
34
|
+
config.plugins.push(vuetify({ autoImport: true }));
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
|
|
39
|
+
vite: {
|
|
40
|
+
vue: {
|
|
41
|
+
template: {
|
|
42
|
+
transformAssetUrls,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goweekdays/layer-common",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"main": "./nuxt.config.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "nuxi dev .playground",
|
|
9
|
+
"dev:prepare": "nuxt prepare .playground",
|
|
10
|
+
"build": "nuxt build .playground",
|
|
11
|
+
"generate": "nuxt generate .playground",
|
|
12
|
+
"preview": "nuxt preview .playground",
|
|
13
|
+
"release": "yarn run build && changeset publish"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@changesets/cli": "^2.27.9",
|
|
17
|
+
"@nuxt/eslint": "latest",
|
|
18
|
+
"eslint": "^9.14.0",
|
|
19
|
+
"nuxt": "^3.13.2",
|
|
20
|
+
"typescript": "^5.6.3",
|
|
21
|
+
"vite-plugin-vuetify": "^2.0.4",
|
|
22
|
+
"vue": "latest",
|
|
23
|
+
"vuetify": "^3.7.3"
|
|
24
|
+
},
|
|
25
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@mdi/font": "^7.4.47",
|
|
28
|
+
"sass": "^1.80.6"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/plugins/API.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export default defineNuxtPlugin(() => {
|
|
2
|
+
const { cookieConfig } = useRuntimeConfig().public;
|
|
3
|
+
|
|
4
|
+
const api = $fetch.create({
|
|
5
|
+
baseURL: "/",
|
|
6
|
+
retry: 1,
|
|
7
|
+
retryStatusCodes: [401],
|
|
8
|
+
retryDelay: 500,
|
|
9
|
+
onRequest({ options }) {
|
|
10
|
+
const accessToken = useCookie("accessToken", cookieConfig).value;
|
|
11
|
+
options.headers = accessToken
|
|
12
|
+
? { Authorization: `Bearer ${accessToken}` }
|
|
13
|
+
: {};
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
async onResponseError({ response }) {
|
|
17
|
+
if (response.status === 401) {
|
|
18
|
+
await $fetch("/api/auth/refresh", {
|
|
19
|
+
baseURL: "/",
|
|
20
|
+
method: "POST",
|
|
21
|
+
server: false,
|
|
22
|
+
credentials: "include",
|
|
23
|
+
body: JSON.stringify({ token: useCookie("refreshToken", cookieConfig).value }),
|
|
24
|
+
|
|
25
|
+
onResponse({ response }) {
|
|
26
|
+
if (response.status === 200) {
|
|
27
|
+
useCookie("accessToken", cookieConfig).value =
|
|
28
|
+
response._data.token;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Expose to useNuxtApp().$api
|
|
37
|
+
return {
|
|
38
|
+
provide: {
|
|
39
|
+
api,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// import this after install `@mdi/font` package
|
|
2
|
+
import "@mdi/font/css/materialdesignicons.css";
|
|
3
|
+
|
|
4
|
+
import "vuetify/styles";
|
|
5
|
+
import { createVuetify } from "vuetify";
|
|
6
|
+
import { VStepperVertical } from "vuetify/labs/VStepperVertical";
|
|
7
|
+
|
|
8
|
+
const defaultTheme = {
|
|
9
|
+
dark: false,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default defineNuxtPlugin((app) => {
|
|
13
|
+
const vuetify = createVuetify({
|
|
14
|
+
defaults: {
|
|
15
|
+
VTextField: {
|
|
16
|
+
variant: "outlined",
|
|
17
|
+
density: "comfortable",
|
|
18
|
+
},
|
|
19
|
+
VAutocomplete: {
|
|
20
|
+
variant: "outlined",
|
|
21
|
+
density: "comfortable",
|
|
22
|
+
},
|
|
23
|
+
VSelect: {
|
|
24
|
+
variant: "outlined",
|
|
25
|
+
density: "comfortable",
|
|
26
|
+
},
|
|
27
|
+
VTextarea: {
|
|
28
|
+
variant: "outlined",
|
|
29
|
+
density: "comfortable",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
theme: {
|
|
33
|
+
defaultTheme: "defaultTheme",
|
|
34
|
+
themes: {
|
|
35
|
+
defaultTheme,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
components: {
|
|
39
|
+
VStepperVertical,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
app.vueApp.use(vuetify);
|
|
44
|
+
});
|
package/tsconfig.json
ADDED
package/types/local.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
declare type TUser = {
|
|
2
|
+
_id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
password: string;
|
|
5
|
+
firstName: string;
|
|
6
|
+
middleName?: string;
|
|
7
|
+
lastName: string;
|
|
8
|
+
suffix?: string;
|
|
9
|
+
birthMonth?: string;
|
|
10
|
+
birthDate?: string;
|
|
11
|
+
birthYear?: string;
|
|
12
|
+
profile?: string;
|
|
13
|
+
defaultOrg?: string;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
status: string;
|
|
17
|
+
deletedAt: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
declare type TToken = {
|
|
21
|
+
accessToken: string;
|
|
22
|
+
refreshToken: string;
|
|
23
|
+
id: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
declare type TNavigationRoute = {
|
|
27
|
+
name: string;
|
|
28
|
+
params?: TKeyValuePair;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
declare type TNavigationItem = {
|
|
32
|
+
title: string;
|
|
33
|
+
icon?: string;
|
|
34
|
+
route?: TNavigationRoute;
|
|
35
|
+
children?: TNavigationItem[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
declare type TKeyValuePair<
|
|
39
|
+
K extends string | number | symbol = string,
|
|
40
|
+
V = string | number | object | Array | TKeyValuePair
|
|
41
|
+
> = {
|
|
42
|
+
[key in K]: V;
|
|
43
|
+
};
|
package/types/org.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
declare type TPermissionCheck =
|
|
2
|
+
| boolean
|
|
3
|
+
| ((user: TRole, data?: any) => boolean);
|
|
4
|
+
|
|
5
|
+
declare type TPermission = {
|
|
6
|
+
check: TPermissionCheck;
|
|
7
|
+
description: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
declare type TPermissions = {
|
|
11
|
+
[resource: string]: {
|
|
12
|
+
[action: string]: TPermission;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
declare type TRole = {
|
|
17
|
+
_id?: string;
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
permissions: string[];
|
|
21
|
+
createdAt?: string;
|
|
22
|
+
updatedAt?: string;
|
|
23
|
+
deletedAt?: string;
|
|
24
|
+
};
|
package/types/role.d.ts
ADDED