@dev.smartpricing/message-composer-utils 3.1.2
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/dist/types/api/aiIntegration.js +7 -0
- package/dist/types/api/aiIntegration.js.map +1 -0
- package/dist/types/api/apiMessages.js +2 -0
- package/dist/types/api/apiMessages.js.map +1 -0
- package/dist/types/api/index.js +3 -0
- package/dist/types/api/index.js.map +1 -0
- package/dist/types/app.js +23 -0
- package/dist/types/app.js.map +1 -0
- package/dist/types/brand.js +31 -0
- package/dist/types/brand.js.map +1 -0
- package/dist/types/dynamic-values.js +5 -0
- package/dist/types/dynamic-values.js.map +1 -0
- package/dist/types/emailblocks.v1.defaults.js +83 -0
- package/dist/types/emailblocks.v1.defaults.js.map +1 -0
- package/dist/types/emailblocks.v1.js +339 -0
- package/dist/types/emailblocks.v1.js.map +1 -0
- package/dist/types/index.js +16 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/kafka-events.js +2 -0
- package/dist/types/kafka-events.js.map +1 -0
- package/dist/types/language.js +2 -0
- package/dist/types/language.js.map +1 -0
- package/dist/types/media.js +6 -0
- package/dist/types/media.js.map +1 -0
- package/dist/types/message.js +41 -0
- package/dist/types/message.js.map +1 -0
- package/dist/types/meta-template.js +2 -0
- package/dist/types/meta-template.js.map +1 -0
- package/dist/types/meta-webhook.js +2 -0
- package/dist/types/meta-webhook.js.map +1 -0
- package/dist/types/submission-job.js +2 -0
- package/dist/types/submission-job.js.map +1 -0
- package/dist/types/tag.js +2 -0
- package/dist/types/tag.js.map +1 -0
- package/dist/types/utils.js +2 -0
- package/dist/types/utils.js.map +1 -0
- package/dist/types/whatsappblocks.v1.js +24 -0
- package/dist/types/whatsappblocks.v1.js.map +1 -0
- package/dist/utils/array.js +26 -0
- package/dist/utils/array.js.map +1 -0
- package/dist/utils/async.js +22 -0
- package/dist/utils/async.js.map +1 -0
- package/dist/utils/date.js +37 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/index.js +34 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/intl.js +37 -0
- package/dist/utils/intl.js.map +1 -0
- package/dist/utils/meta-status.js +21 -0
- package/dist/utils/meta-status.js.map +1 -0
- package/dist/utils/number.js +24 -0
- package/dist/utils/number.js.map +1 -0
- package/dist/utils/object.js +87 -0
- package/dist/utils/object.js.map +1 -0
- package/dist/utils/string.js +22 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/utils/templates/brand-cascade.js +116 -0
- package/dist/utils/templates/brand-cascade.js.map +1 -0
- package/dist/utils/templates/email-compiler.js +126 -0
- package/dist/utils/templates/email-compiler.js.map +1 -0
- package/dist/utils/templates/email-mjml.js +444 -0
- package/dist/utils/templates/email-mjml.js.map +1 -0
- package/dist/utils/templates/email.js +580 -0
- package/dist/utils/templates/email.js.map +1 -0
- package/dist/utils/templates/html-sanitizer.js +95 -0
- package/dist/utils/templates/html-sanitizer.js.map +1 -0
- package/dist/utils/templates/messages.js +33 -0
- package/dist/utils/templates/messages.js.map +1 -0
- package/dist/utils/templates/meta.js +246 -0
- package/dist/utils/templates/meta.js.map +1 -0
- package/dist/utils/templates/tiptap.js +167 -0
- package/dist/utils/templates/tiptap.js.map +1 -0
- package/dist/utils/templates/whatsapp-compiler.js +168 -0
- package/dist/utils/templates/whatsapp-compiler.js.map +1 -0
- package/dist/utils/templates/whatsapp.js +329 -0
- package/dist/utils/templates/whatsapp.js.map +1 -0
- package/dist/utils/validations.js +39 -0
- package/dist/utils/validations.js.map +1 -0
- package/package.json +43 -0
- package/src/types/api/aiIntegration.ts +59 -0
- package/src/types/api/apiMessages.ts +22 -0
- package/src/types/api/index.ts +2 -0
- package/src/types/app.ts +76 -0
- package/src/types/brand.ts +80 -0
- package/src/types/dynamic-values.ts +47 -0
- package/src/types/emailblocks.v1.defaults.ts +123 -0
- package/src/types/emailblocks.v1.ts +655 -0
- package/src/types/index.ts +15 -0
- package/src/types/kafka-events.ts +46 -0
- package/src/types/language.ts +9 -0
- package/src/types/media.ts +26 -0
- package/src/types/message.ts +635 -0
- package/src/types/meta-template.ts +50 -0
- package/src/types/meta-webhook.ts +32 -0
- package/src/types/submission-job.ts +30 -0
- package/src/types/tag.ts +8 -0
- package/src/types/utils.ts +14 -0
- package/src/types/whatsappblocks.v1.ts +102 -0
- package/src/utils/array.ts +33 -0
- package/src/utils/async.ts +30 -0
- package/src/utils/date.ts +39 -0
- package/src/utils/index.ts +44 -0
- package/src/utils/intl.ts +51 -0
- package/src/utils/meta-status.ts +24 -0
- package/src/utils/number.ts +36 -0
- package/src/utils/object.ts +101 -0
- package/src/utils/string.ts +24 -0
- package/src/utils/templates/brand-cascade.ts +156 -0
- package/src/utils/templates/email-compiler.ts +155 -0
- package/src/utils/templates/email-mjml.ts +602 -0
- package/src/utils/templates/email.ts +713 -0
- package/src/utils/templates/html-sanitizer.ts +121 -0
- package/src/utils/templates/messages.ts +44 -0
- package/src/utils/templates/meta.ts +336 -0
- package/src/utils/templates/tiptap.ts +247 -0
- package/src/utils/templates/whatsapp-compiler.ts +212 -0
- package/src/utils/templates/whatsapp.ts +408 -0
- package/src/utils/validations.ts +49 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface MetaWebhookPayload {
|
|
2
|
+
entry: MetaWebhookEntry;
|
|
3
|
+
object: "whatsapp_business_account";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface MetaWebhookEntry {
|
|
7
|
+
id: string;
|
|
8
|
+
time: number;
|
|
9
|
+
changes: MetaWebhookChange[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MetaWebhookChange {
|
|
13
|
+
value: MetaTemplateStatusUpdate | MetaTemplateCategoryUpdate;
|
|
14
|
+
field: "message_template_status_update" | "template_category_update";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MetaTemplateCategoryUpdate {
|
|
18
|
+
message_template_id: number;
|
|
19
|
+
message_template_name: string;
|
|
20
|
+
message_template_language: string;
|
|
21
|
+
new_category: "MARKETING" | "UTILITY";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type MetaTemplateStatus = "APPROVED" | "REJECTED" | "PENDING";
|
|
25
|
+
export type InternalTemplateStatus = "approved" | "rejected" | "pending";
|
|
26
|
+
export interface MetaTemplateStatusUpdate {
|
|
27
|
+
event: MetaTemplateStatus;
|
|
28
|
+
message_template_id: number;
|
|
29
|
+
message_template_name: string;
|
|
30
|
+
message_template_language: string;
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type SubmissionJobStatus =
|
|
2
|
+
| "queued"
|
|
3
|
+
| "processing"
|
|
4
|
+
| "completed"
|
|
5
|
+
| "failed";
|
|
6
|
+
|
|
7
|
+
export interface SubmissionJob {
|
|
8
|
+
id: string;
|
|
9
|
+
message_group_id: string;
|
|
10
|
+
status: SubmissionJobStatus;
|
|
11
|
+
submitted_count: number;
|
|
12
|
+
failed_count: number;
|
|
13
|
+
errors: Array<{
|
|
14
|
+
messageId: string;
|
|
15
|
+
error: string;
|
|
16
|
+
}>;
|
|
17
|
+
meta_config: {
|
|
18
|
+
wabaId: string;
|
|
19
|
+
accessToken: string;
|
|
20
|
+
apiVersion?: string;
|
|
21
|
+
};
|
|
22
|
+
created_at: string;
|
|
23
|
+
updated_at: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SubmissionJobResponse {
|
|
27
|
+
jobId: string;
|
|
28
|
+
status: "queued";
|
|
29
|
+
message: string;
|
|
30
|
+
}
|
package/src/types/tag.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type WithDbTimestamps<T> = {
|
|
2
|
+
[k in keyof T]: T[k] extends Date ? string : T[k];
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export type DbEntity = {
|
|
6
|
+
id: string;
|
|
7
|
+
created_at: Date;
|
|
8
|
+
updated_at: Date;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type PostEntity<T extends DbEntity> = Omit<
|
|
12
|
+
T,
|
|
13
|
+
"id" | "created_at" | "updated_at"
|
|
14
|
+
>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { JSONContent } from "@tiptap/core";
|
|
2
|
+
|
|
3
|
+
export const WA_HEADER_TYPES_V1 = [
|
|
4
|
+
"none",
|
|
5
|
+
"text",
|
|
6
|
+
"video",
|
|
7
|
+
"image",
|
|
8
|
+
"document",
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
export type WaHeaderTypeV1 = (typeof WA_HEADER_TYPES_V1)[number];
|
|
12
|
+
|
|
13
|
+
interface WaHeaderTypeV1Base {
|
|
14
|
+
type: WaHeaderTypeV1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface WaHeaderTypeV1Text extends WaHeaderTypeV1Base {
|
|
18
|
+
type: "text";
|
|
19
|
+
content: JSONContent | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WaHeaderTypeV1Url extends WaHeaderTypeV1Base {
|
|
23
|
+
type: "video" | "document" | "image";
|
|
24
|
+
url: string;
|
|
25
|
+
metaMediaId?: string;
|
|
26
|
+
smartchatMediaId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WaHeaderTypeV1None extends WaHeaderTypeV1Base {
|
|
30
|
+
type: "none";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type HeaderBlockV1 =
|
|
34
|
+
| WaHeaderTypeV1Text
|
|
35
|
+
| WaHeaderTypeV1Url
|
|
36
|
+
| WaHeaderTypeV1None;
|
|
37
|
+
|
|
38
|
+
export type BodyBlockV1 = {
|
|
39
|
+
enabled: true;
|
|
40
|
+
content: undefined | JSONContent;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type FooterBlockV1 = {
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
content: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const WA_BUTTON_TYPES_V1 = [
|
|
49
|
+
"unsubscribe",
|
|
50
|
+
"phone",
|
|
51
|
+
"link",
|
|
52
|
+
"quick_reply",
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
export type WaButtonTypeV1 = (typeof WA_BUTTON_TYPES_V1)[number];
|
|
56
|
+
|
|
57
|
+
interface WaButtonV1Base {
|
|
58
|
+
id: string;
|
|
59
|
+
type: WaButtonTypeV1;
|
|
60
|
+
text: string; // max 20 characters
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface WaButtonV1Unsubscribe extends WaButtonV1Base {
|
|
64
|
+
type: "unsubscribe";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface WaButtonV1Phone extends WaButtonV1Base {
|
|
68
|
+
type: "phone";
|
|
69
|
+
phoneNumber: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface WaButtonV1Link extends WaButtonV1Base {
|
|
73
|
+
type: "link";
|
|
74
|
+
url: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface WaButtonV1QuickReply extends WaButtonV1Base {
|
|
78
|
+
type: "quick_reply";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type WaButtonV1 =
|
|
82
|
+
| WaButtonV1Unsubscribe
|
|
83
|
+
| WaButtonV1Phone
|
|
84
|
+
| WaButtonV1Link
|
|
85
|
+
| WaButtonV1QuickReply;
|
|
86
|
+
|
|
87
|
+
export type ButtonBlockV1 = {
|
|
88
|
+
enabled: boolean;
|
|
89
|
+
buttons: WaButtonV1[];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Validation constants
|
|
93
|
+
export const WA_HEADER_TEXT_MAX_LENGTH = 60;
|
|
94
|
+
export const WA_BODY_MAX_LENGTH = 1024;
|
|
95
|
+
export const WA_FOOTER_MAX_LENGTH = 60;
|
|
96
|
+
export const WA_BUTTON_TEXT_MAX_LENGTH = 20;
|
|
97
|
+
export const WA_BUTTON_MAX_LINK_BUTTONS = 1;
|
|
98
|
+
export const WA_BUTTON_MAX_PHONE_BUTTONS = 1;
|
|
99
|
+
export const WA_BUTTON_MAX_UNSUBSCRIBE_BUTTONS = 1;
|
|
100
|
+
export const WA_BUTTON_MAX_QUICK_REPLY_BUTTONS = 3;
|
|
101
|
+
|
|
102
|
+
export const WA_BUTTON_MAX_BUTTONS = 10;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Array utility functions
|
|
2
|
+
export const unique = <T>(array: T[]): T[] => {
|
|
3
|
+
return [...new Set(array)];
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const uniqueBy = <T, K>(array: T[], keyFn: (item: T) => K): T[] => {
|
|
7
|
+
const seen = new Set<K>();
|
|
8
|
+
return array.filter((item) => {
|
|
9
|
+
const key = keyFn(item);
|
|
10
|
+
if (seen.has(key)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
seen.add(key);
|
|
14
|
+
return true;
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const groupBy = <T, K extends string | number>(
|
|
19
|
+
array: T[],
|
|
20
|
+
keyFn: (item: T) => K,
|
|
21
|
+
): Record<K, T[]> => {
|
|
22
|
+
return array.reduce(
|
|
23
|
+
(groups, item) => {
|
|
24
|
+
const key = keyFn(item);
|
|
25
|
+
if (!groups[key]) {
|
|
26
|
+
groups[key] = [];
|
|
27
|
+
}
|
|
28
|
+
groups[key].push(item);
|
|
29
|
+
return groups;
|
|
30
|
+
},
|
|
31
|
+
{} as Record<K, T[]>,
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
export const sleep = (ms: number): Promise<void> => {
|
|
4
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const debounce = <T extends (...args: any[]) => void>(
|
|
8
|
+
func: T,
|
|
9
|
+
delay: number,
|
|
10
|
+
): ((...args: Parameters<T>) => void) => {
|
|
11
|
+
let timeoutId: ReturnType<typeof setTimeout>;
|
|
12
|
+
return (...args: Parameters<T>) => {
|
|
13
|
+
clearTimeout(timeoutId);
|
|
14
|
+
timeoutId = setTimeout(() => func(...args), delay);
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const throttle = <T extends (...args: any[]) => void>(
|
|
19
|
+
func: T,
|
|
20
|
+
limit: number,
|
|
21
|
+
): ((...args: Parameters<T>) => void) => {
|
|
22
|
+
let inThrottle: boolean;
|
|
23
|
+
return (...args: Parameters<T>) => {
|
|
24
|
+
if (!inThrottle) {
|
|
25
|
+
func(...args);
|
|
26
|
+
inThrottle = true;
|
|
27
|
+
setTimeout(() => (inThrottle = false), limit);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function parseDate(date: Date | string | number): Date {
|
|
2
|
+
if (typeof date === "number") {
|
|
3
|
+
return new Date(date);
|
|
4
|
+
}
|
|
5
|
+
if (typeof date === "string") {
|
|
6
|
+
return new Date(date);
|
|
7
|
+
}
|
|
8
|
+
return date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Date utility functions
|
|
12
|
+
export const formatDate = (date: Date | string | number): string => {
|
|
13
|
+
date = parseDate(date);
|
|
14
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
15
|
+
year: "numeric",
|
|
16
|
+
month: "2-digit",
|
|
17
|
+
day: "2-digit",
|
|
18
|
+
}).format(date);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const formatDateTime = (date: Date | string): string => {
|
|
22
|
+
date = parseDate(date);
|
|
23
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
24
|
+
year: "numeric",
|
|
25
|
+
month: "2-digit",
|
|
26
|
+
day: "2-digit",
|
|
27
|
+
hour: "2-digit",
|
|
28
|
+
minute: "2-digit",
|
|
29
|
+
}).format(date);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const formatTime = (date: Date | string): string => {
|
|
33
|
+
date = parseDate(date);
|
|
34
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
35
|
+
hour: "2-digit",
|
|
36
|
+
minute: "2-digit",
|
|
37
|
+
second: "2-digit",
|
|
38
|
+
}).format(date);
|
|
39
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Export all utilities by category
|
|
2
|
+
export * from "./async.js";
|
|
3
|
+
export * from "./array.js";
|
|
4
|
+
export * from "./date.js";
|
|
5
|
+
export * from "./number.js";
|
|
6
|
+
export * from "./object.js";
|
|
7
|
+
export * from "./string.js";
|
|
8
|
+
export * from "./intl.js";
|
|
9
|
+
export * from "./templates/messages.js";
|
|
10
|
+
export * from "./templates/whatsapp.js";
|
|
11
|
+
export * from "./templates/email.js";
|
|
12
|
+
export * from "./templates/meta.js";
|
|
13
|
+
export * from "./templates/tiptap.js";
|
|
14
|
+
export * from "./templates/whatsapp-compiler.js";
|
|
15
|
+
export * from "./templates/email-compiler.js";
|
|
16
|
+
export * from "./templates/email-mjml.js";
|
|
17
|
+
export * from "./templates/brand-cascade.js";
|
|
18
|
+
export * from "./templates/html-sanitizer.js";
|
|
19
|
+
export * from "./validations.js";
|
|
20
|
+
export * from "./meta-status.js";
|
|
21
|
+
|
|
22
|
+
// Also export as namespaced modules for cleaner imports
|
|
23
|
+
export * as AsyncUtils from "./async.js";
|
|
24
|
+
export * as ArrayUtils from "./array.js";
|
|
25
|
+
export * as DateUtils from "./date.js";
|
|
26
|
+
export * as NumberUtils from "./number.js";
|
|
27
|
+
export * as ObjectUtils from "./object.js";
|
|
28
|
+
export * as StringUtils from "./string.js";
|
|
29
|
+
export * as WhatsappUtils from "./templates/whatsapp.js";
|
|
30
|
+
export * as ValidationsUtils from "./validations.js";
|
|
31
|
+
export * as IntlUtils from "./intl.js";
|
|
32
|
+
|
|
33
|
+
// Re-export specific functions with aliases for clarity
|
|
34
|
+
export {
|
|
35
|
+
normalizeParameters,
|
|
36
|
+
compileWhatsappTemplate,
|
|
37
|
+
extractRequiredParameters,
|
|
38
|
+
validateParameters as validateWhatsappParameters,
|
|
39
|
+
} from "./templates/whatsapp-compiler.js";
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
extractEmailParameters,
|
|
43
|
+
validateEmailParameters,
|
|
44
|
+
} from "./templates/email-compiler.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function getIntlDisplayName(settings: {
|
|
2
|
+
locale: string;
|
|
3
|
+
type: Intl.DisplayNamesType;
|
|
4
|
+
of: string;
|
|
5
|
+
}) {
|
|
6
|
+
if (settings.of.trim() === "") {
|
|
7
|
+
return "-";
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const x = new Intl.DisplayNames([settings.locale], {
|
|
11
|
+
type: settings.type,
|
|
12
|
+
}).of(settings.of.toUpperCase());
|
|
13
|
+
return x;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
console.log(e);
|
|
16
|
+
return settings.of;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getLanguageDisplayName(
|
|
21
|
+
language: string | null,
|
|
22
|
+
locale: string = "en",
|
|
23
|
+
) {
|
|
24
|
+
return getIntlDisplayName({
|
|
25
|
+
locale: locale,
|
|
26
|
+
type: "language",
|
|
27
|
+
of: language ?? "",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getCountryDisplayName(
|
|
32
|
+
country: string | null,
|
|
33
|
+
locale: string = "en",
|
|
34
|
+
) {
|
|
35
|
+
return getIntlDisplayName({
|
|
36
|
+
locale: locale,
|
|
37
|
+
type: "region",
|
|
38
|
+
of: country ?? "",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getNationalityDisplayName(
|
|
43
|
+
nationality: string | null,
|
|
44
|
+
locale: string = "en",
|
|
45
|
+
) {
|
|
46
|
+
return getIntlDisplayName({
|
|
47
|
+
locale: locale,
|
|
48
|
+
type: "region",
|
|
49
|
+
of: nationality ?? "",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { MetaTemplateStatus, InternalTemplateStatus } from "../types/index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts Meta API status to internal status format
|
|
5
|
+
* @param metaStatus The status from Meta API
|
|
6
|
+
* @returns The internal status representation
|
|
7
|
+
*/
|
|
8
|
+
export function mapMetaStatusToInternal(
|
|
9
|
+
metaStatus?: MetaTemplateStatus,
|
|
10
|
+
): InternalTemplateStatus | null {
|
|
11
|
+
if (!metaStatus) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
switch (metaStatus) {
|
|
15
|
+
case "PENDING":
|
|
16
|
+
return "pending";
|
|
17
|
+
case "APPROVED":
|
|
18
|
+
return "approved";
|
|
19
|
+
case "REJECTED":
|
|
20
|
+
return "rejected";
|
|
21
|
+
default:
|
|
22
|
+
return "pending";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Number utility functions
|
|
2
|
+
export const randomBetween = (min: number, max: number): number => {
|
|
3
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const clamp = (value: number, min: number, max: number): number => {
|
|
7
|
+
return Math.min(Math.max(value, min), max);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const formatNumber = (
|
|
11
|
+
value: number,
|
|
12
|
+
options: Intl.NumberFormatOptions = {},
|
|
13
|
+
): string => {
|
|
14
|
+
return new Intl.NumberFormat("en-US", options).format(value);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const formatCurrency = (
|
|
18
|
+
value: number,
|
|
19
|
+
currency: string = "USD",
|
|
20
|
+
): string => {
|
|
21
|
+
return new Intl.NumberFormat("en-US", {
|
|
22
|
+
style: "currency",
|
|
23
|
+
currency,
|
|
24
|
+
}).format(value);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const formatPercentage = (
|
|
28
|
+
value: number,
|
|
29
|
+
decimals: number = 2,
|
|
30
|
+
): string => {
|
|
31
|
+
return new Intl.NumberFormat("en-US", {
|
|
32
|
+
style: "percent",
|
|
33
|
+
minimumFractionDigits: decimals,
|
|
34
|
+
maximumFractionDigits: decimals,
|
|
35
|
+
}).format(value);
|
|
36
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object utility functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if value is a function
|
|
7
|
+
*/
|
|
8
|
+
export function isFunction<T>(
|
|
9
|
+
value: unknown,
|
|
10
|
+
): value is (...args: unknown[]) => T {
|
|
11
|
+
return typeof value === "function";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Gets the value at path of object. Similar to lodash get.
|
|
16
|
+
*/
|
|
17
|
+
export function get<T = unknown>(
|
|
18
|
+
object: unknown,
|
|
19
|
+
path: string | string[],
|
|
20
|
+
): T | undefined {
|
|
21
|
+
if (!object || !path) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const pathArray = Array.isArray(path) ? path : path.split(".");
|
|
26
|
+
|
|
27
|
+
let result = object;
|
|
28
|
+
for (let i = 0; i < pathArray.length; i++) {
|
|
29
|
+
if (result == null) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
result = result[pathArray[i] as keyof typeof result];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result as T;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks if a value exists and is not null or undefined
|
|
40
|
+
*/
|
|
41
|
+
export function isDefined<T>(value: T | null | undefined): value is T {
|
|
42
|
+
return value !== null && value !== undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function isEmpty(value: unknown): boolean {
|
|
46
|
+
if (value === null || value === undefined) return true;
|
|
47
|
+
if (typeof value === "string") return value.trim().length === 0;
|
|
48
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
49
|
+
if (typeof value === "object") return Object.keys(value).length === 0;
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function deepClone<T>(value: T): T {
|
|
54
|
+
try {
|
|
55
|
+
return structuredClone(value) as T;
|
|
56
|
+
} catch {
|
|
57
|
+
// Fallback to JSON serialization for objects that can't be structuredClone'd
|
|
58
|
+
// (e.g., Vue reactive proxies, objects with symbols, etc.)
|
|
59
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Deep equality comparison between two values
|
|
65
|
+
*/
|
|
66
|
+
export function deepEqual(a: unknown, b: unknown): boolean {
|
|
67
|
+
// Handle identical references and primitive equality
|
|
68
|
+
if (a === b) return true;
|
|
69
|
+
|
|
70
|
+
// Handle null/undefined
|
|
71
|
+
if (a == null || b == null) return a === b;
|
|
72
|
+
|
|
73
|
+
// Handle dates
|
|
74
|
+
if (a instanceof Date && b instanceof Date) {
|
|
75
|
+
return a.getTime() === b.getTime();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle arrays
|
|
79
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
80
|
+
if (a.length !== b.length) return false;
|
|
81
|
+
return a.every((item, index) => deepEqual(item, b[index]));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Handle objects
|
|
85
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
86
|
+
const keysA = Object.keys(a as object);
|
|
87
|
+
const keysB = Object.keys(b as object);
|
|
88
|
+
|
|
89
|
+
if (keysA.length !== keysB.length) return false;
|
|
90
|
+
|
|
91
|
+
return keysA.every(
|
|
92
|
+
(key) =>
|
|
93
|
+
Object.prototype.hasOwnProperty.call(b, key) &&
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
deepEqual((a as any)[key], (b as any)[key]),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Handle other types (strings, numbers, etc)
|
|
100
|
+
return a === b;
|
|
101
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// String utility functions
|
|
2
|
+
export const capitalize = (str: string): string => {
|
|
3
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const truncate = (str: string, length: number): string => {
|
|
7
|
+
if (str.length <= length) return str;
|
|
8
|
+
return str.substring(0, length) + "...";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const snakeCase = (str: string): string => {
|
|
12
|
+
return str
|
|
13
|
+
.replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
14
|
+
.replace(/\s+/g, "_")
|
|
15
|
+
.toLowerCase();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const removeWhitespace = (str: string): string => {
|
|
19
|
+
return str.replaceAll(" ", "");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const isEmptyOrWhitespace = (str: string): boolean => {
|
|
23
|
+
return !str || !str.trim();
|
|
24
|
+
};
|