@goweekdays/layer-common 1.5.2 → 1.5.4
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/CHANGELOG.md +12 -0
- package/composables/useOrg.ts +13 -0
- package/composables/useSubscription.ts +56 -15
- package/composables/useUtils.ts +178 -0
- package/composables/useVerification.ts +1 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @goweekdays/layer-common
|
|
2
2
|
|
|
3
|
+
## 1.5.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cff30d5: Add updatePromoCode function to useSubscription
|
|
8
|
+
|
|
9
|
+
## 1.5.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 8a5737a: Remove seats property from orgSetupFee parameter
|
|
14
|
+
|
|
3
15
|
## 1.5.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/composables/useOrg.ts
CHANGED
|
@@ -63,6 +63,18 @@ export default function useOrg() {
|
|
|
63
63
|
promoCode: "",
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
+
function getCompanies(search = "") {
|
|
67
|
+
return useNuxtApp().$api<Record<string, any>[]>(
|
|
68
|
+
"/api/organizations/company",
|
|
69
|
+
{
|
|
70
|
+
method: "GET",
|
|
71
|
+
query: {
|
|
72
|
+
search,
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
return {
|
|
67
79
|
org,
|
|
68
80
|
add,
|
|
@@ -70,5 +82,6 @@ export default function useOrg() {
|
|
|
70
82
|
getByUserId,
|
|
71
83
|
getById,
|
|
72
84
|
updateById,
|
|
85
|
+
getCompanies,
|
|
73
86
|
};
|
|
74
87
|
}
|
|
@@ -34,20 +34,6 @@ export default function useSubscription() {
|
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function updateSeatById({ id = "", seats = 0, amount = 0, user = "" } = {}) {
|
|
38
|
-
return useNuxtApp().$api<Record<string, any>>(
|
|
39
|
-
`/api/subscriptions/id/${id}/seats`,
|
|
40
|
-
{
|
|
41
|
-
method: "PATCH",
|
|
42
|
-
body: {
|
|
43
|
-
seats,
|
|
44
|
-
amount,
|
|
45
|
-
user,
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
37
|
function getAll({ page = 1, limit = 20, search = "", status = "" } = {}) {
|
|
52
38
|
return useNuxtApp().$api<Record<string, any>>(`/api/subscriptions`, {
|
|
53
39
|
method: "GET",
|
|
@@ -60,11 +46,66 @@ export default function useSubscription() {
|
|
|
60
46
|
});
|
|
61
47
|
}
|
|
62
48
|
|
|
49
|
+
function computeFee(value: {
|
|
50
|
+
seats: number;
|
|
51
|
+
promoCode?: string;
|
|
52
|
+
org: string;
|
|
53
|
+
plan: string;
|
|
54
|
+
}) {
|
|
55
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
56
|
+
`/api/subscriptions/compute`,
|
|
57
|
+
{
|
|
58
|
+
method: "POST",
|
|
59
|
+
body: value,
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function subscribe(value: {
|
|
65
|
+
seats: number;
|
|
66
|
+
promoCode?: string;
|
|
67
|
+
org: string;
|
|
68
|
+
plan: string;
|
|
69
|
+
user: string;
|
|
70
|
+
}) {
|
|
71
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/subscriptions`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
body: value,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function updateSeats(value: {
|
|
78
|
+
seats: number;
|
|
79
|
+
promoCode?: string;
|
|
80
|
+
org: string;
|
|
81
|
+
plan: string;
|
|
82
|
+
user: string;
|
|
83
|
+
}) {
|
|
84
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/subscriptions`, {
|
|
85
|
+
method: "PATCH",
|
|
86
|
+
body: value,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function updatePromoCode(value: {
|
|
91
|
+
promoCode: string;
|
|
92
|
+
org: string;
|
|
93
|
+
user: string;
|
|
94
|
+
}) {
|
|
95
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/subscriptions/promo`, {
|
|
96
|
+
method: "PATCH",
|
|
97
|
+
body: value,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
63
101
|
return {
|
|
64
102
|
subscription,
|
|
103
|
+
computeFee,
|
|
104
|
+
subscribe,
|
|
105
|
+
updateSeats,
|
|
106
|
+
updatePromoCode,
|
|
65
107
|
getAll,
|
|
66
108
|
getByOrg,
|
|
67
109
|
getTransactionsById,
|
|
68
|
-
updateSeatById,
|
|
69
110
|
};
|
|
70
111
|
}
|
package/composables/useUtils.ts
CHANGED
|
@@ -81,6 +81,12 @@ export default function useUtils() {
|
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function requireSlug(text: string) {
|
|
85
|
+
// Allows lowercase letters, dots, underscores, numbers and hyphens
|
|
86
|
+
const pattern = /^[a-z0-9._-]+$/;
|
|
87
|
+
return pattern.test(text) || "Invalid value";
|
|
88
|
+
}
|
|
89
|
+
|
|
84
90
|
function back() {
|
|
85
91
|
useRouter().back();
|
|
86
92
|
}
|
|
@@ -277,6 +283,174 @@ export default function useUtils() {
|
|
|
277
283
|
return (v && v >= 1) || "Value must be greater or equal to 1";
|
|
278
284
|
}
|
|
279
285
|
|
|
286
|
+
function getCurrencySymbol(currency: string): string {
|
|
287
|
+
if (!currency) return "";
|
|
288
|
+
return new Intl.NumberFormat(undefined, {
|
|
289
|
+
style: "currency",
|
|
290
|
+
currency,
|
|
291
|
+
})
|
|
292
|
+
.formatToParts(0)
|
|
293
|
+
.find((part) => part.type === "currency")!.value;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* ============================================================
|
|
297
|
+
Payment Promo Code Generator (Strict TypeScript)
|
|
298
|
+
Format:
|
|
299
|
+
PREFIX-BRAND-WORD-NUMBER-CHECK
|
|
300
|
+
|
|
301
|
+
Type → Prefix mapping:
|
|
302
|
+
fixed → FIX
|
|
303
|
+
flat → FLAT
|
|
304
|
+
volume → VOL
|
|
305
|
+
|
|
306
|
+
Example:
|
|
307
|
+
FIX-COCO-PREMIUM-742-K
|
|
308
|
+
============================================================ */
|
|
309
|
+
|
|
310
|
+
/* -------------------- Types -------------------- */
|
|
311
|
+
|
|
312
|
+
type NonEmptyString = string & { readonly __brand: unique symbol };
|
|
313
|
+
|
|
314
|
+
type PromoType = "fixed" | "flat" | "volume";
|
|
315
|
+
|
|
316
|
+
type PaymentPromoOptions = Readonly<{
|
|
317
|
+
type: PromoType;
|
|
318
|
+
brandLength?: number;
|
|
319
|
+
numericLength?: number;
|
|
320
|
+
}>;
|
|
321
|
+
|
|
322
|
+
/* -------------------- Type → Prefix Map -------------------- */
|
|
323
|
+
|
|
324
|
+
const TYPE_PREFIX: Record<PromoType, string> = {
|
|
325
|
+
fixed: "FIX",
|
|
326
|
+
flat: "FLAT",
|
|
327
|
+
volume: "VOL",
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/* -------------------- Business Word Bank -------------------- */
|
|
331
|
+
|
|
332
|
+
const WORDS = [
|
|
333
|
+
"PRIME",
|
|
334
|
+
"STANDARD",
|
|
335
|
+
"SELECT",
|
|
336
|
+
"CORE",
|
|
337
|
+
"PLUS",
|
|
338
|
+
"ADVANTAGE",
|
|
339
|
+
"PREMIUM",
|
|
340
|
+
"BASIC",
|
|
341
|
+
"ENTERPRISE",
|
|
342
|
+
"BUSINESS",
|
|
343
|
+
"PRO",
|
|
344
|
+
"VALUE",
|
|
345
|
+
"GOLD",
|
|
346
|
+
"SILVER",
|
|
347
|
+
"PLATINUM",
|
|
348
|
+
"ACCESS",
|
|
349
|
+
"PARTNER",
|
|
350
|
+
"PREFERRED",
|
|
351
|
+
"ELITE",
|
|
352
|
+
"ESSENTIAL",
|
|
353
|
+
"ADVANCE",
|
|
354
|
+
"MAX",
|
|
355
|
+
"OPTIMAL",
|
|
356
|
+
"GROWTH",
|
|
357
|
+
"SECURE",
|
|
358
|
+
"TRUST",
|
|
359
|
+
] as const;
|
|
360
|
+
|
|
361
|
+
type PromoWord = (typeof WORDS)[number];
|
|
362
|
+
|
|
363
|
+
/* -------------------- Guards -------------------- */
|
|
364
|
+
|
|
365
|
+
function assertNonEmptyString(
|
|
366
|
+
value: unknown
|
|
367
|
+
): asserts value is NonEmptyString {
|
|
368
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
369
|
+
throw new Error("Promo base must be a non-empty string");
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* -------------------- Utilities -------------------- */
|
|
374
|
+
|
|
375
|
+
function normalizeBase(input: NonEmptyString): string {
|
|
376
|
+
return input
|
|
377
|
+
.toUpperCase()
|
|
378
|
+
.replace(/\s+/g, "")
|
|
379
|
+
.replace(/[^A-Z0-9]/g, "");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function randomFrom<T>(arr: readonly T[]): T {
|
|
383
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function numericEntropy(length: number): string {
|
|
387
|
+
const seed = `${Date.now()}${Math.random()}`.replace(/\D/g, "");
|
|
388
|
+
return seed.slice(-length).padStart(length, "0");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Lightweight checksum
|
|
393
|
+
* Detects tampering / typos
|
|
394
|
+
* Not cryptographic by design
|
|
395
|
+
*/
|
|
396
|
+
function checksum(value: string): string {
|
|
397
|
+
let sum = 0;
|
|
398
|
+
for (let i = 0; i < value.length; i++) {
|
|
399
|
+
sum += value.charCodeAt(i);
|
|
400
|
+
}
|
|
401
|
+
return String.fromCharCode(65 + (sum % 26));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* -------------------- Generator -------------------- */
|
|
405
|
+
|
|
406
|
+
function generatePaymentPromoCode(
|
|
407
|
+
base: unknown,
|
|
408
|
+
options: PaymentPromoOptions
|
|
409
|
+
): string {
|
|
410
|
+
if (base === undefined || base === null) {
|
|
411
|
+
return "";
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// if no type return empty string
|
|
415
|
+
if (!options.type) {
|
|
416
|
+
return "";
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
assertNonEmptyString(base);
|
|
420
|
+
|
|
421
|
+
const { type, brandLength = 4, numericLength = 3 } = options;
|
|
422
|
+
|
|
423
|
+
const prefix = TYPE_PREFIX[type];
|
|
424
|
+
const normalized = normalizeBase(base);
|
|
425
|
+
|
|
426
|
+
const brand = normalized.slice(0, brandLength).padEnd(brandLength, "X");
|
|
427
|
+
|
|
428
|
+
const word: PromoWord = randomFrom(WORDS);
|
|
429
|
+
const number = numericEntropy(numericLength);
|
|
430
|
+
|
|
431
|
+
const core = `${prefix}-${brand}-${word}-${number}`;
|
|
432
|
+
const check = checksum(core);
|
|
433
|
+
|
|
434
|
+
return `${core}-${check}`;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function copyToClipboard(value: string) {
|
|
438
|
+
if (!value) return;
|
|
439
|
+
if (!navigator.clipboard) {
|
|
440
|
+
console.error("Clipboard API not supported");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
navigator.clipboard
|
|
445
|
+
.writeText(value)
|
|
446
|
+
.then(() => {
|
|
447
|
+
console.log("Copied to clipboard");
|
|
448
|
+
})
|
|
449
|
+
.catch((err) => {
|
|
450
|
+
console.error("Failed to copy:", err);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
280
454
|
return {
|
|
281
455
|
requiredRule,
|
|
282
456
|
emailRule,
|
|
@@ -304,5 +478,9 @@ export default function useUtils() {
|
|
|
304
478
|
setRouteParams,
|
|
305
479
|
positiveNumberRule,
|
|
306
480
|
validateKey,
|
|
481
|
+
getCurrencySymbol,
|
|
482
|
+
requireSlug,
|
|
483
|
+
generatePaymentPromoCode,
|
|
484
|
+
copyToClipboard,
|
|
307
485
|
};
|
|
308
486
|
}
|
|
@@ -46,9 +46,7 @@ export default function useUser() {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function orgSetupFee(
|
|
49
|
-
value: Pick<TOrg, "name" | "email" | "contact" | "createdBy">
|
|
50
|
-
seats: number;
|
|
51
|
-
}
|
|
49
|
+
value: Pick<TOrg, "name" | "email" | "contact" | "createdBy">
|
|
52
50
|
) {
|
|
53
51
|
return useNuxtApp().$api<Record<string, any>>(
|
|
54
52
|
"/api/verifications/org-setup-fee",
|