@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.
- 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 +20 -0
- package/CHANGELOG.md +7 -0
- package/README.md +73 -0
- package/app.vue +3 -0
- package/components/AddPaymentMethod.vue +585 -0
- package/components/BtnUploadFile.vue +139 -0
- package/components/ConfirmDialog.vue +66 -0
- package/components/Container/Standard.vue +33 -0
- package/components/Input/Date.vue +177 -0
- package/components/Input/ListGroupSelection.vue +93 -0
- package/components/Input/NewDate.vue +123 -0
- package/components/Input/Number.vue +124 -0
- package/components/Input/Password.vue +35 -0
- package/components/InputLabel.vue +18 -0
- package/components/InvitationMain.vue +195 -0
- package/components/Layout/Header.vue +285 -0
- package/components/Layout/NavigationDrawer.vue +52 -0
- package/components/LinkHome.vue +9 -0
- package/components/ListItem.vue +35 -0
- package/components/LocalPagination.vue +41 -0
- package/components/MemberMain.vue +452 -0
- package/components/NavigationItem.vue +73 -0
- package/components/PlaceholderComponent.vue +34 -0
- package/components/RolePermissionFormCreate.vue +179 -0
- package/components/RolePermissionFormPreviewUpdate.vue +184 -0
- package/components/RolePermissionMain.vue +376 -0
- package/components/Snackbar.vue +23 -0
- package/components/SpecificAttr.vue +57 -0
- package/components/Std/Pagination.vue +52 -0
- package/components/SwitchContext.vue +109 -0
- package/components/SwitchOrg.vue +159 -0
- package/components/TableList.vue +130 -0
- package/composables/useAddress.ts +144 -0
- package/composables/useChartOfAccount.ts +62 -0
- package/composables/useCommonPermission.ts +130 -0
- package/composables/useFile.ts +29 -0
- package/composables/useInvoice.ts +42 -0
- package/composables/useLocal.ts +63 -0
- package/composables/useLocalAuth.ts +157 -0
- package/composables/useLocalSetup.ts +46 -0
- package/composables/useMember.ts +107 -0
- package/composables/useOrder.ts +22 -0
- package/composables/useOrg.ts +106 -0
- package/composables/useOrgPermission.ts +27 -0
- package/composables/usePayment.ts +22 -0
- package/composables/usePaymentMethod.ts +347 -0
- package/composables/usePermission.ts +54 -0
- package/composables/usePrice.ts +15 -0
- package/composables/usePromoCode.ts +43 -0
- package/composables/useRecapPermission.ts +26 -0
- package/composables/useRole.ts +89 -0
- package/composables/useSchoolPermission.ts +13 -0
- package/composables/useSubscription.ts +264 -0
- package/composables/useUser.ts +102 -0
- package/composables/useUtils.ts +294 -0
- package/composables/useVerification.ts +19 -0
- package/error.vue +41 -0
- package/eslint.config.js +3 -0
- package/layouts/plain.vue +7 -0
- package/middleware/01.auth.ts +14 -0
- package/middleware/org.ts +16 -0
- package/nuxt.config.ts +48 -0
- package/package.json +35 -0
- package/pages/index.vue +3 -0
- package/pages/payment-method-cancel-link.vue +31 -0
- package/pages/payment-method-failed-link.vue +31 -0
- package/pages/payment-method-linked.vue +31 -0
- package/pages/require-organization-membership.vue +47 -0
- package/pages/unauthorized.vue +29 -0
- package/plugins/API.ts +58 -0
- package/plugins/iconify.client.ts +5 -0
- package/plugins/vuetify.ts +55 -0
- package/public/bdo-logo.svg +4 -0
- package/public/bpi-logo.svg +74 -0
- package/public/chinabank-logo.svg +120 -0
- package/public/gcash-logo.png +0 -0
- package/public/gcash-logo.svg +65 -0
- package/public/grabpay-logo.svg +99 -0
- package/public/paymaya-logo.jpg +0 -0
- package/public/paymaya-logo.png +0 -0
- package/public/paymaya-logo.svg +25 -0
- package/public/qrph-c567ff0f-ab6d-4662-86bf-24c6c731d8a8-logo.svg +20 -0
- package/public/rcbc-logo.svg +15 -0
- package/public/shopeepay-logo.svg +89 -0
- package/public/ubp-logo.svg +88 -0
- package/tsconfig.json +3 -0
- package/types/address.d.ts +13 -0
- package/types/invoice.d.ts +28 -0
- package/types/local.d.ts +25 -0
- package/types/member.d.ts +12 -0
- package/types/org.d.ts +13 -0
- package/types/payment-method.d.ts +11 -0
- package/types/payment.d.ts +18 -0
- package/types/permission.d.ts +14 -0
- package/types/price.d.ts +17 -0
- package/types/promo-code.d.ts +19 -0
- package/types/role.d.ts +13 -0
- package/types/subscription.d.ts +29 -0
- package/types/user.d.ts +21 -0
- package/types/verification.d.ts +15 -0
- package/types/xendit.d.ts +3 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
export default function useSubscription() {
|
|
2
|
+
function createSubscriptionPlan(
|
|
3
|
+
seats: number,
|
|
4
|
+
trialDays: number,
|
|
5
|
+
productId: string
|
|
6
|
+
) {
|
|
7
|
+
return useNuxtApp().$api("/api/subscriptions/plan", {
|
|
8
|
+
method: "POST",
|
|
9
|
+
body: {
|
|
10
|
+
seats,
|
|
11
|
+
trialDays,
|
|
12
|
+
productId,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function add(subscriptionId: string, user: string) {
|
|
18
|
+
return useNuxtApp().$api("/api/subscriptions", {
|
|
19
|
+
method: "POST",
|
|
20
|
+
body: {
|
|
21
|
+
subscriptionId,
|
|
22
|
+
user,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function addBillingContactById(id: string, email: string) {
|
|
28
|
+
return useNuxtApp().$api(`/api/subscriptions/billing-contact/${id}`, {
|
|
29
|
+
method: "PUT",
|
|
30
|
+
body: {
|
|
31
|
+
email,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function updateBillingContactByAddedAt(
|
|
37
|
+
id: string,
|
|
38
|
+
addedAt: string,
|
|
39
|
+
email: string
|
|
40
|
+
) {
|
|
41
|
+
return useNuxtApp().$api(
|
|
42
|
+
`/api/subscriptions/billing-contact/${id}/added-at/${addedAt}`,
|
|
43
|
+
{
|
|
44
|
+
method: "PUT",
|
|
45
|
+
body: {
|
|
46
|
+
email,
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function deleteBillingContactByAddedAt(id: string, addedAt: string) {
|
|
53
|
+
return useNuxtApp().$api(
|
|
54
|
+
`/api/subscriptions/billing-contact/${id}/added-at/${addedAt}`,
|
|
55
|
+
{
|
|
56
|
+
method: "DELETE",
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getById(id: string) {
|
|
62
|
+
return useNuxtApp().$api<TSubscription>(`/api/subscriptions/id/${id}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getByAffiliateId(id: string) {
|
|
66
|
+
return useNuxtApp().$api<TSubscription>(
|
|
67
|
+
`/api/subscriptions/affiliate/${id}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getByOrgId(id: string) {
|
|
72
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
73
|
+
`/api/subscriptions/org/${id}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getSubscriptions() {
|
|
78
|
+
return useNuxtApp().$api("/api/subscriptions");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getSubscriptionStatusById(id: string) {
|
|
82
|
+
return useNuxtApp().$api<Record<string, string>>(
|
|
83
|
+
`/api/subscriptions/status/${id}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function cancelSubscription(id: string) {
|
|
88
|
+
return useNuxtApp().$api(`/api/subscriptions/cancel/${id}`, {
|
|
89
|
+
method: "DELETE",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const affiliateSubscription = useState(
|
|
94
|
+
"affiliateSubscription",
|
|
95
|
+
() => "inactive"
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
function updatePromoCodeById(id: string, promoCode: string) {
|
|
99
|
+
return useNuxtApp().$api(`/api/subscriptions/promo-code/${id}`, {
|
|
100
|
+
method: "PUT",
|
|
101
|
+
body: {
|
|
102
|
+
promoCode,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function updateStatusById(id: string, status: string) {
|
|
108
|
+
return useNuxtApp().$api(`/api/subscriptions/status/${id}`, {
|
|
109
|
+
method: "PUT",
|
|
110
|
+
body: {
|
|
111
|
+
status,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function updatePaymentMethodById(id: string, paymentMethodId: string) {
|
|
117
|
+
return useNuxtApp().$api(`/api/subscriptions/payment-method/${id}`, {
|
|
118
|
+
method: "PUT",
|
|
119
|
+
body: {
|
|
120
|
+
paymentMethodId,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function affSubscriptionStatus() {
|
|
126
|
+
const { currentUser } = useLocalAuth();
|
|
127
|
+
|
|
128
|
+
if (currentUser.value && currentUser.value._id) {
|
|
129
|
+
try {
|
|
130
|
+
const result = await getByAffiliateId(currentUser.value._id);
|
|
131
|
+
affiliateSubscription.value = result.status as string;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error("failed to get the subscription", error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const orgSubscription = useState("orgSubscription", () => "inactive");
|
|
139
|
+
|
|
140
|
+
async function orgSubscriptionStatus() {
|
|
141
|
+
const { currentOrg } = useOrg();
|
|
142
|
+
|
|
143
|
+
if (currentOrg.value) {
|
|
144
|
+
try {
|
|
145
|
+
const data = await getByOrgId(currentOrg.value);
|
|
146
|
+
orgSubscription.value = data?.status as string;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error("failed to get the subscription", error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
type TSub = {
|
|
154
|
+
customer_id: string;
|
|
155
|
+
amount: number;
|
|
156
|
+
payment_method_id: string;
|
|
157
|
+
payment_method_type: string;
|
|
158
|
+
payment_method_channel: string;
|
|
159
|
+
payment_method_expiry_month?: string;
|
|
160
|
+
payment_method_expiry_year?: string;
|
|
161
|
+
payment_method_cvv?: string;
|
|
162
|
+
payment_method_cardholder_name?: string;
|
|
163
|
+
currency?: string;
|
|
164
|
+
seats?: number;
|
|
165
|
+
organization?: TOrg;
|
|
166
|
+
billingAddress: TAddress;
|
|
167
|
+
promoCode?: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
function initOrgSubscription(value: TSub) {
|
|
171
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
172
|
+
"/api/subscriptions/organization",
|
|
173
|
+
{
|
|
174
|
+
method: "POST",
|
|
175
|
+
body: value,
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function initAffiliateSubscription(value: TSub) {
|
|
181
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
182
|
+
"/api/subscriptions/affiliate",
|
|
183
|
+
{
|
|
184
|
+
method: "POST",
|
|
185
|
+
body: value,
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function updateSeatsById({
|
|
191
|
+
transactionId = "",
|
|
192
|
+
subscriptionId = "",
|
|
193
|
+
seats = 0,
|
|
194
|
+
amount = 0,
|
|
195
|
+
} = {}) {
|
|
196
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
197
|
+
`/api/subscriptions/seats/${subscriptionId}`,
|
|
198
|
+
{
|
|
199
|
+
method: "PUT",
|
|
200
|
+
body: {
|
|
201
|
+
seats,
|
|
202
|
+
amount,
|
|
203
|
+
transactionId,
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function processSubscriptionPayment(id: string, invoice: string) {
|
|
210
|
+
return useNuxtApp().$api(`/api/subscriptions/payment/id/${id}`, {
|
|
211
|
+
method: "PUT",
|
|
212
|
+
body: {
|
|
213
|
+
invoice,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function addOrgSubscription(value: {
|
|
219
|
+
user: string;
|
|
220
|
+
transactionId?: string;
|
|
221
|
+
promoCode?: string;
|
|
222
|
+
seats: number;
|
|
223
|
+
perSeatPrice: number;
|
|
224
|
+
currency: string;
|
|
225
|
+
org: {
|
|
226
|
+
name: string;
|
|
227
|
+
email: string;
|
|
228
|
+
contact: string;
|
|
229
|
+
busInst: string;
|
|
230
|
+
type: string;
|
|
231
|
+
};
|
|
232
|
+
}) {
|
|
233
|
+
return useNuxtApp().$api("/api/subscriptions/subscribe", {
|
|
234
|
+
method: "POST",
|
|
235
|
+
body: value,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
add,
|
|
241
|
+
getById,
|
|
242
|
+
getByOrgId,
|
|
243
|
+
getSubscriptions,
|
|
244
|
+
affSubscriptionStatus,
|
|
245
|
+
affiliateSubscription,
|
|
246
|
+
orgSubscriptionStatus,
|
|
247
|
+
orgSubscription,
|
|
248
|
+
getSubscriptionStatusById,
|
|
249
|
+
cancelSubscription,
|
|
250
|
+
initAffiliateSubscription,
|
|
251
|
+
initOrgSubscription,
|
|
252
|
+
getByAffiliateId,
|
|
253
|
+
updateSeatsById,
|
|
254
|
+
updatePromoCodeById,
|
|
255
|
+
updateStatusById,
|
|
256
|
+
updatePaymentMethodById,
|
|
257
|
+
addBillingContactById,
|
|
258
|
+
updateBillingContactByAddedAt,
|
|
259
|
+
deleteBillingContactByAddedAt,
|
|
260
|
+
processSubscriptionPayment,
|
|
261
|
+
createSubscriptionPlan,
|
|
262
|
+
addOrgSubscription,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export default function useUser() {
|
|
2
|
+
function inviteUser({
|
|
3
|
+
email = "",
|
|
4
|
+
app = "",
|
|
5
|
+
role = "",
|
|
6
|
+
roleName = "",
|
|
7
|
+
org = "",
|
|
8
|
+
orgName = "",
|
|
9
|
+
} = {}) {
|
|
10
|
+
return useNuxtApp().$api<Record<string, any>>("/api/auth/invite", {
|
|
11
|
+
method: "POST",
|
|
12
|
+
body: { email, app, role, roleName, org, orgName },
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function updateName({ firstName = "", lastName = "" } = {}) {
|
|
17
|
+
return useNuxtApp().$api<Record<string, any>>("/api/users/name", {
|
|
18
|
+
method: "PUT",
|
|
19
|
+
body: { firstName, lastName },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function updateBirthday({ month = "", day = 0, year = 0 } = {}) {
|
|
24
|
+
return useNuxtApp().$api<Record<string, any>>("/api/users/birthday", {
|
|
25
|
+
method: "PUT",
|
|
26
|
+
body: { month, day, year },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function updateGender(gender = "") {
|
|
31
|
+
return useNuxtApp().$api<Record<string, any>>("/api/users/gender", {
|
|
32
|
+
method: "PUT",
|
|
33
|
+
body: { gender },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function updateEmail(email = "") {
|
|
38
|
+
return useNuxtApp().$api<Record<string, any>>("/api/users/email", {
|
|
39
|
+
method: "PUT",
|
|
40
|
+
body: { email },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function updateContact(contact = "") {
|
|
45
|
+
return useNuxtApp().$api<Record<string, any>>("/api/users/contact", {
|
|
46
|
+
method: "PUT",
|
|
47
|
+
body: { contact },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function updateUserFieldById(id = "", field = "", value = "") {
|
|
52
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/users/field/${id}`, {
|
|
53
|
+
method: "PATCH",
|
|
54
|
+
body: { field, value },
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getUsers({
|
|
59
|
+
status = "active",
|
|
60
|
+
type = "",
|
|
61
|
+
search = "",
|
|
62
|
+
page = 1,
|
|
63
|
+
} = {}) {
|
|
64
|
+
return useNuxtApp().$api<Record<string, any>>("/api/users", {
|
|
65
|
+
method: "GET",
|
|
66
|
+
query: { status, search, page, type },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getById(id = "") {
|
|
71
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/users/id/${id}`, {
|
|
72
|
+
method: "GET",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function createUserByVerification({
|
|
77
|
+
firstName = "",
|
|
78
|
+
lastName = "",
|
|
79
|
+
password = "",
|
|
80
|
+
id = "",
|
|
81
|
+
referralCode = "",
|
|
82
|
+
type = "",
|
|
83
|
+
} = {}) {
|
|
84
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/users/invite/${id}`, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
body: { firstName, lastName, password, referralCode, type },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
inviteUser,
|
|
92
|
+
updateName,
|
|
93
|
+
updateBirthday,
|
|
94
|
+
updateGender,
|
|
95
|
+
updateEmail,
|
|
96
|
+
updateContact,
|
|
97
|
+
updateUserFieldById,
|
|
98
|
+
getUsers,
|
|
99
|
+
createUserByVerification,
|
|
100
|
+
getById,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
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 validateDate = (value: string): boolean | string => {
|
|
10
|
+
const dateRegex = /^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/;
|
|
11
|
+
|
|
12
|
+
if (!dateRegex.test(value)) return "Invalid date format (MM/DD/YYYY)";
|
|
13
|
+
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function requireListRule(value: string[]) {
|
|
18
|
+
return value.length ? "" : "Required";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const minOneMonthAdvance = (value: string): boolean | string => {
|
|
22
|
+
if (!/^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/.test(value)) {
|
|
23
|
+
return "Invalid date format (MM/DD/YYYY)";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const [month, day, year] = value.split("/").map(Number);
|
|
27
|
+
const selectedDate = new Date(year, month - 1, day);
|
|
28
|
+
|
|
29
|
+
const currentDate = new Date();
|
|
30
|
+
const minDate = new Date();
|
|
31
|
+
minDate.setMonth(currentDate.getMonth() + 1); // 1 month in advance
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
selectedDate >= minDate || "Date must be at least 1 month in advance"
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const passwordRule = (v: string) =>
|
|
39
|
+
v.length >= 8 || "Password must be at least 8 characters long";
|
|
40
|
+
|
|
41
|
+
const confirmPasswordRule =
|
|
42
|
+
(newPassword: Ref<string>, confirmPassword: Ref<string>) => () =>
|
|
43
|
+
confirmPassword.value === newPassword.value || "Passwords must match";
|
|
44
|
+
|
|
45
|
+
const validateEmail = (email: Ref<string>, emailErrors: Ref<string[]>) => {
|
|
46
|
+
emailErrors.value = [];
|
|
47
|
+
|
|
48
|
+
if (!email.value) {
|
|
49
|
+
emailErrors.value.push("Please input your email");
|
|
50
|
+
} else {
|
|
51
|
+
const result = emailRule(email.value);
|
|
52
|
+
if (result !== true) {
|
|
53
|
+
emailErrors.value.push(result);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const validateWord = (value: string): boolean | string => {
|
|
59
|
+
if (value.trim().length < 3) return "Must be at least 3 characters";
|
|
60
|
+
return true;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const validatePassword = (
|
|
64
|
+
password: Ref<string>,
|
|
65
|
+
passwordErrors: Ref<string[]>
|
|
66
|
+
) => {
|
|
67
|
+
passwordErrors.value = [];
|
|
68
|
+
|
|
69
|
+
if (!password.value) {
|
|
70
|
+
passwordErrors.value.push("Please input your password");
|
|
71
|
+
} else if (!passwordRule(password.value)) {
|
|
72
|
+
passwordErrors.value.push("Password must be at least 8 characters long");
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function back() {
|
|
77
|
+
useRouter().back();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function debounce<T extends (...args: any[]) => void>(
|
|
81
|
+
func: T,
|
|
82
|
+
wait: number
|
|
83
|
+
): (...args: Parameters<T>) => void {
|
|
84
|
+
let timeoutId: ReturnType<typeof setTimeout>;
|
|
85
|
+
|
|
86
|
+
return function (...args: Parameters<T>) {
|
|
87
|
+
clearTimeout(timeoutId);
|
|
88
|
+
timeoutId = setTimeout(() => func(...args), wait);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function insertBetween(arr: any, elementToInsert: any) {
|
|
93
|
+
return arr.flatMap((item: any, index: number, array: any) =>
|
|
94
|
+
index < array.length - 1 ? [item, elementToInsert] : [item]
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getNameInitials(name: string) {
|
|
99
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
100
|
+
return ""; // Default fallback for invalid input
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const words = name.trim().split(/\s+/); // Split by spaces
|
|
104
|
+
|
|
105
|
+
if (words.length === 1) {
|
|
106
|
+
return words[0].slice(0, 2).toUpperCase(); // Take first two letters of the single word
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return words
|
|
110
|
+
.map((word) => word[0]?.toUpperCase()) // Take the first letter of each word and capitalize
|
|
111
|
+
.join("")
|
|
112
|
+
.slice(0, 2); // Limit to 2 characters
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function getDimensions(
|
|
116
|
+
file: File
|
|
117
|
+
): Promise<{ width: number; height: number }> {
|
|
118
|
+
const img = new Image();
|
|
119
|
+
img.src = URL.createObjectURL(file);
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
img.onload = () => {
|
|
122
|
+
const width = img.width;
|
|
123
|
+
const height = img.height;
|
|
124
|
+
resolve({ width, height });
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
img.onerror = () => {
|
|
128
|
+
reject(new Error("Failed to read image dimensions"));
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function getCountries() {
|
|
134
|
+
try {
|
|
135
|
+
const countries = await useNuxtApp().$api<Array<Record<string, any>>>(
|
|
136
|
+
"https://restcountries.com/v3.1/all?fields=name,currencies,idd",
|
|
137
|
+
{ method: "GET" }
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const uniqueCountries: Map<
|
|
141
|
+
string,
|
|
142
|
+
{ index: string; name: string; code: string }
|
|
143
|
+
> = new Map();
|
|
144
|
+
|
|
145
|
+
countries.forEach((country) => {
|
|
146
|
+
let suffixes = country.idd?.suffixes?.[0] || "";
|
|
147
|
+
let code = `${country.idd?.root || ""}${suffixes}`;
|
|
148
|
+
let name = country.name?.common || "";
|
|
149
|
+
|
|
150
|
+
if (name && code && !uniqueCountries.has(code)) {
|
|
151
|
+
uniqueCountries.set(code, { index: `${name}-${code}`, name, code });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return Array.from(uniqueCountries.values()).sort((a, b) =>
|
|
156
|
+
a.name.localeCompare(b.name)
|
|
157
|
+
);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(error);
|
|
160
|
+
throw new Error("Failed to fetch countries.");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
165
|
+
minimumFractionDigits: 2,
|
|
166
|
+
maximumFractionDigits: 2,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
function formatNumber(
|
|
170
|
+
amount: number,
|
|
171
|
+
options: {
|
|
172
|
+
currency?: string;
|
|
173
|
+
locale?: string;
|
|
174
|
+
useSymbol?: boolean;
|
|
175
|
+
decimalPlaces?: number;
|
|
176
|
+
} = {}
|
|
177
|
+
): string {
|
|
178
|
+
const {
|
|
179
|
+
currency,
|
|
180
|
+
locale = "en-US",
|
|
181
|
+
useSymbol = false,
|
|
182
|
+
decimalPlaces = 2,
|
|
183
|
+
} = options;
|
|
184
|
+
|
|
185
|
+
return new Intl.NumberFormat(locale, {
|
|
186
|
+
style: useSymbol && currency ? "currency" : "decimal",
|
|
187
|
+
currency: currency || "USD", // Default currency (ignored if `useSymbol` is false)
|
|
188
|
+
minimumFractionDigits: decimalPlaces,
|
|
189
|
+
maximumFractionDigits: decimalPlaces,
|
|
190
|
+
}).format(amount);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 🔹 Examples:
|
|
194
|
+
// console.log(formatNumber(1234.56)); // "1,234.56" (comma separator, 2 decimals)
|
|
195
|
+
// console.log(formatNumber(1234.56, { decimalPlaces: 0 })); // "1,234" (no decimals)
|
|
196
|
+
// console.log(formatNumber(1234.56, { useSymbol: true, currency: "USD" })); // "$1,234.56"
|
|
197
|
+
// console.log(formatNumber(1234.56, { useSymbol: true, currency: "PHP", decimalPlaces: 0 })); // "₱1,234"
|
|
198
|
+
// console.log(formatNumber(1234.56, { useSymbol: false, decimalPlaces: 0 })); // "1,234"
|
|
199
|
+
// console.log(formatNumber(1234.56, { useSymbol: true, currency: "EUR", locale: "de-DE" })); // "1.234,56 €"
|
|
200
|
+
// console.log(formatNumber(1234.56, { useSymbol: true, currency: "EUR", locale: "de-DE", decimalPlaces: 0 })); // "1.234 €"
|
|
201
|
+
|
|
202
|
+
function computeTieredCost(
|
|
203
|
+
seats: number,
|
|
204
|
+
tiers: { min: number; max: number; price: number }[],
|
|
205
|
+
remainingDays?: number,
|
|
206
|
+
totalDaysInMonth?: number
|
|
207
|
+
) {
|
|
208
|
+
let totalCost = 0;
|
|
209
|
+
|
|
210
|
+
for (let i = 1; i <= seats; i++) {
|
|
211
|
+
for (const { min, max, price } of tiers) {
|
|
212
|
+
const effectiveMax = max === 0 ? Infinity : max;
|
|
213
|
+
|
|
214
|
+
if (i >= min && i <= effectiveMax) {
|
|
215
|
+
// Prorate the cost for this seat based on its tier price
|
|
216
|
+
if (remainingDays && totalDaysInMonth) {
|
|
217
|
+
totalCost += price * (remainingDays / totalDaysInMonth);
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
totalCost += price;
|
|
222
|
+
break; // Stop checking once we apply the correct tier pricing
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return totalCost; // This now returns the total prorated cost
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function convertPermissionsToArray(permissions: TPermissions) {
|
|
231
|
+
return Object.entries(permissions).flatMap(([resource, actions]) =>
|
|
232
|
+
Object.keys(actions).map((action) => `${resource}:${action}`)
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function extractMonthYear(expiry: string) {
|
|
237
|
+
const [month, year] = expiry.split("/");
|
|
238
|
+
return {
|
|
239
|
+
month: month.padStart(2, "0"),
|
|
240
|
+
year: 2000 + parseInt(year, 10),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function getRouteParam(field = "") {
|
|
245
|
+
if (!field) return "";
|
|
246
|
+
|
|
247
|
+
return (useRoute().params[field] as string) ?? "";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function replaceMatch(str: string, match: string, replace: string) {
|
|
251
|
+
return str.replace(new RegExp(match, "g"), replace);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function setRouteParams(...args: Array<Record<string, string>>) {
|
|
255
|
+
const params: Record<string, string> = {};
|
|
256
|
+
|
|
257
|
+
args.forEach((arg) => {
|
|
258
|
+
Object.entries(arg).forEach(([key, value]) => {
|
|
259
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
260
|
+
params[key] = value;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return params;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
requiredRule,
|
|
270
|
+
emailRule,
|
|
271
|
+
passwordRule,
|
|
272
|
+
confirmPasswordRule,
|
|
273
|
+
validateEmail,
|
|
274
|
+
validatePassword,
|
|
275
|
+
back,
|
|
276
|
+
debounce,
|
|
277
|
+
insertBetween,
|
|
278
|
+
getNameInitials,
|
|
279
|
+
getDimensions,
|
|
280
|
+
validateWord,
|
|
281
|
+
getCountries,
|
|
282
|
+
formatter,
|
|
283
|
+
formatNumber,
|
|
284
|
+
validateDate,
|
|
285
|
+
minOneMonthAdvance,
|
|
286
|
+
computeTieredCost,
|
|
287
|
+
requireListRule,
|
|
288
|
+
convertPermissionsToArray,
|
|
289
|
+
extractMonthYear,
|
|
290
|
+
getRouteParam,
|
|
291
|
+
replaceMatch,
|
|
292
|
+
setRouteParams,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default function useUser() {
|
|
2
|
+
function getVerifications({
|
|
3
|
+
status = "pending",
|
|
4
|
+
type = "user-invite",
|
|
5
|
+
search = "",
|
|
6
|
+
page = 1,
|
|
7
|
+
email = "",
|
|
8
|
+
app = "",
|
|
9
|
+
} = {}) {
|
|
10
|
+
return useNuxtApp().$api<Record<string, any>>("/api/verifications", {
|
|
11
|
+
method: "GET",
|
|
12
|
+
query: { status, search, page, type, email, app },
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
getVerifications,
|
|
18
|
+
};
|
|
19
|
+
}
|