@goweekdays/layer-common 1.5.3 → 1.5.5

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @goweekdays/layer-common
2
2
 
3
+ ## 1.5.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 74535b7: Update subscription API endpoint for seat management
8
+
9
+ ## 1.5.4
10
+
11
+ ### Patch Changes
12
+
13
+ - cff30d5: Add updatePromoCode function to useSubscription
14
+
3
15
  ## 1.5.3
4
16
 
5
17
  ### Patch Changes
@@ -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/seats`, {
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
  }
@@ -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
  }
@@ -287,6 +293,164 @@ export default function useUtils() {
287
293
  .find((part) => part.type === "currency")!.value;
288
294
  }
289
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
+
290
454
  return {
291
455
  requiredRule,
292
456
  emailRule,
@@ -315,5 +479,8 @@ export default function useUtils() {
315
479
  positiveNumberRule,
316
480
  validateKey,
317
481
  getCurrencySymbol,
482
+ requireSlug,
483
+ generatePaymentPromoCode,
484
+ copyToClipboard,
318
485
  };
319
486
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@goweekdays/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.5.3",
5
+ "version": "1.5.5",
6
6
  "main": "./nuxt.config.ts",
7
7
  "publishConfig": {
8
8
  "access": "public"