@voyantjs/crm 0.1.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/LICENSE +109 -0
- package/README.md +47 -0
- package/dist/booking-extension.d.ts +123 -0
- package/dist/booking-extension.d.ts.map +1 -0
- package/dist/booking-extension.js +86 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/routes/accounts.d.ts +1203 -0
- package/dist/routes/accounts.d.ts.map +1 -0
- package/dist/routes/accounts.js +226 -0
- package/dist/routes/activities.d.ts +299 -0
- package/dist/routes/activities.d.ts.map +1 -0
- package/dist/routes/activities.js +61 -0
- package/dist/routes/custom-fields.d.ts +256 -0
- package/dist/routes/custom-fields.d.ts.map +1 -0
- package/dist/routes/custom-fields.js +46 -0
- package/dist/routes/index.d.ts +2671 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +14 -0
- package/dist/routes/opportunities.d.ts +387 -0
- package/dist/routes/opportunities.d.ts.map +1 -0
- package/dist/routes/opportunities.js +69 -0
- package/dist/routes/pipelines.d.ts +292 -0
- package/dist/routes/pipelines.d.ts.map +1 -0
- package/dist/routes/pipelines.js +58 -0
- package/dist/routes/quotes.d.ts +283 -0
- package/dist/routes/quotes.d.ts.map +1 -0
- package/dist/routes/quotes.js +51 -0
- package/dist/schema.d.ts +3478 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +515 -0
- package/dist/service/accounts.d.ts +982 -0
- package/dist/service/accounts.d.ts.map +1 -0
- package/dist/service/accounts.js +509 -0
- package/dist/service/activities.d.ts +486 -0
- package/dist/service/activities.d.ts.map +1 -0
- package/dist/service/activities.js +114 -0
- package/dist/service/custom-fields.d.ts +118 -0
- package/dist/service/custom-fields.d.ts.map +1 -0
- package/dist/service/custom-fields.js +88 -0
- package/dist/service/helpers.d.ts +22 -0
- package/dist/service/helpers.d.ts.map +1 -0
- package/dist/service/helpers.js +39 -0
- package/dist/service/index.d.ts +3329 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +14 -0
- package/dist/service/opportunities.d.ts +822 -0
- package/dist/service/opportunities.d.ts.map +1 -0
- package/dist/service/opportunities.js +117 -0
- package/dist/service/pipelines.d.ts +113 -0
- package/dist/service/pipelines.d.ts.map +1 -0
- package/dist/service/pipelines.js +68 -0
- package/dist/service/quotes.d.ts +494 -0
- package/dist/service/quotes.d.ts.map +1 -0
- package/dist/service/quotes.js +69 -0
- package/dist/validation.d.ts +860 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +315 -0
- package/package.json +56 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import type { customFieldDefinitionListQuerySchema, customFieldValueListQuerySchema, insertCustomFieldDefinitionSchema, updateCustomFieldDefinitionSchema, upsertCustomFieldValueSchema } from "../validation.js";
|
|
4
|
+
type CustomFieldDefinitionListQuery = z.infer<typeof customFieldDefinitionListQuerySchema>;
|
|
5
|
+
type CreateCustomFieldDefinitionInput = z.infer<typeof insertCustomFieldDefinitionSchema>;
|
|
6
|
+
type UpdateCustomFieldDefinitionInput = z.infer<typeof updateCustomFieldDefinitionSchema>;
|
|
7
|
+
type CustomFieldValueListQuery = z.infer<typeof customFieldValueListQuerySchema>;
|
|
8
|
+
type UpsertCustomFieldValueInput = z.infer<typeof upsertCustomFieldValueSchema>;
|
|
9
|
+
export declare const customFieldsService: {
|
|
10
|
+
listCustomFieldDefinitions(db: PostgresJsDatabase, query: CustomFieldDefinitionListQuery): Promise<{
|
|
11
|
+
data: {
|
|
12
|
+
id: string;
|
|
13
|
+
entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
|
|
14
|
+
key: string;
|
|
15
|
+
label: string;
|
|
16
|
+
fieldType: "boolean" | "json" | "date" | "text" | "phone" | "set" | "enum" | "varchar" | "double" | "monetary" | "address";
|
|
17
|
+
isRequired: boolean;
|
|
18
|
+
isSearchable: boolean;
|
|
19
|
+
options: {
|
|
20
|
+
label: string;
|
|
21
|
+
value: string;
|
|
22
|
+
}[] | null;
|
|
23
|
+
createdAt: Date;
|
|
24
|
+
updatedAt: Date;
|
|
25
|
+
}[];
|
|
26
|
+
total: number;
|
|
27
|
+
limit: number;
|
|
28
|
+
offset: number;
|
|
29
|
+
}>;
|
|
30
|
+
getCustomFieldDefinitionById(db: PostgresJsDatabase, id: string): Promise<{
|
|
31
|
+
id: string;
|
|
32
|
+
entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
|
|
33
|
+
key: string;
|
|
34
|
+
label: string;
|
|
35
|
+
fieldType: "boolean" | "json" | "date" | "text" | "phone" | "set" | "enum" | "varchar" | "double" | "monetary" | "address";
|
|
36
|
+
isRequired: boolean;
|
|
37
|
+
isSearchable: boolean;
|
|
38
|
+
options: {
|
|
39
|
+
label: string;
|
|
40
|
+
value: string;
|
|
41
|
+
}[] | null;
|
|
42
|
+
createdAt: Date;
|
|
43
|
+
updatedAt: Date;
|
|
44
|
+
} | null>;
|
|
45
|
+
createCustomFieldDefinition(db: PostgresJsDatabase, data: CreateCustomFieldDefinitionInput): Promise<{
|
|
46
|
+
key: string;
|
|
47
|
+
createdAt: Date;
|
|
48
|
+
updatedAt: Date;
|
|
49
|
+
options: {
|
|
50
|
+
label: string;
|
|
51
|
+
value: string;
|
|
52
|
+
}[] | null;
|
|
53
|
+
entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
|
|
54
|
+
label: string;
|
|
55
|
+
id: string;
|
|
56
|
+
fieldType: "boolean" | "json" | "date" | "text" | "phone" | "set" | "enum" | "varchar" | "double" | "monetary" | "address";
|
|
57
|
+
isRequired: boolean;
|
|
58
|
+
isSearchable: boolean;
|
|
59
|
+
} | undefined>;
|
|
60
|
+
updateCustomFieldDefinition(db: PostgresJsDatabase, id: string, data: UpdateCustomFieldDefinitionInput): Promise<{
|
|
61
|
+
id: string;
|
|
62
|
+
entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
|
|
63
|
+
key: string;
|
|
64
|
+
label: string;
|
|
65
|
+
fieldType: "boolean" | "json" | "date" | "text" | "phone" | "set" | "enum" | "varchar" | "double" | "monetary" | "address";
|
|
66
|
+
isRequired: boolean;
|
|
67
|
+
isSearchable: boolean;
|
|
68
|
+
options: {
|
|
69
|
+
label: string;
|
|
70
|
+
value: string;
|
|
71
|
+
}[] | null;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
updatedAt: Date;
|
|
74
|
+
} | null>;
|
|
75
|
+
deleteCustomFieldDefinition(db: PostgresJsDatabase, id: string): Promise<{
|
|
76
|
+
id: string;
|
|
77
|
+
} | null>;
|
|
78
|
+
listCustomFieldValues(db: PostgresJsDatabase, query: CustomFieldValueListQuery): Promise<{
|
|
79
|
+
data: {
|
|
80
|
+
id: string;
|
|
81
|
+
definitionId: string;
|
|
82
|
+
entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
|
|
83
|
+
entityId: string;
|
|
84
|
+
textValue: string | null;
|
|
85
|
+
numberValue: number | null;
|
|
86
|
+
dateValue: string | null;
|
|
87
|
+
booleanValue: boolean | null;
|
|
88
|
+
monetaryValueCents: number | null;
|
|
89
|
+
currencyCode: string | null;
|
|
90
|
+
jsonValue: Record<string, unknown> | string[] | null;
|
|
91
|
+
createdAt: Date;
|
|
92
|
+
updatedAt: Date;
|
|
93
|
+
}[];
|
|
94
|
+
total: number;
|
|
95
|
+
limit: number;
|
|
96
|
+
offset: number;
|
|
97
|
+
}>;
|
|
98
|
+
upsertCustomFieldValue(db: PostgresJsDatabase, definitionId: string, data: UpsertCustomFieldValueInput): Promise<{
|
|
99
|
+
createdAt: Date;
|
|
100
|
+
updatedAt: Date;
|
|
101
|
+
entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
|
|
102
|
+
entityId: string;
|
|
103
|
+
id: string;
|
|
104
|
+
definitionId: string;
|
|
105
|
+
textValue: string | null;
|
|
106
|
+
numberValue: number | null;
|
|
107
|
+
dateValue: string | null;
|
|
108
|
+
booleanValue: boolean | null;
|
|
109
|
+
monetaryValueCents: number | null;
|
|
110
|
+
currencyCode: string | null;
|
|
111
|
+
jsonValue: Record<string, unknown> | string[] | null;
|
|
112
|
+
} | undefined>;
|
|
113
|
+
deleteCustomFieldValue(db: PostgresJsDatabase, id: string): Promise<{
|
|
114
|
+
id: string;
|
|
115
|
+
} | null>;
|
|
116
|
+
};
|
|
117
|
+
export {};
|
|
118
|
+
//# sourceMappingURL=custom-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-fields.d.ts","sourceRoot":"","sources":["../../src/service/custom-fields.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAG5B,OAAO,KAAK,EACV,oCAAoC,EACpC,+BAA+B,EAC/B,iCAAiC,EACjC,iCAAiC,EACjC,4BAA4B,EAC7B,MAAM,kBAAkB,CAAA;AAGzB,KAAK,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAA;AAC1F,KAAK,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAA;AACzF,KAAK,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAA;AACzF,KAAK,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAA;AAChF,KAAK,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAE/E,eAAO,MAAM,mBAAmB;mCACO,kBAAkB,SAAS,8BAA8B;;;;;;;;;;;;;;;;;;;;qCAkBvD,kBAAkB,MAAM,MAAM;;;;;;;;;;;;;;;oCAU/D,kBAAkB,QAChB,gCAAgC;;;;;;;;;;;;;;;oCAOlC,kBAAkB,MAClB,MAAM,QACJ,gCAAgC;;;;;;;;;;;;;;;oCAUF,kBAAkB,MAAM,MAAM;;;8BAQpC,kBAAkB,SAAS,yBAAyB;;;;;;;;;;;;;;;;;;;;+BAsB9E,kBAAkB,gBACR,MAAM,QACd,2BAA2B;;;;;;;;;;;;;;;+BA8BF,kBAAkB,MAAM,MAAM;;;CAOhE,CAAA"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { and, desc, eq, sql } from "drizzle-orm";
|
|
2
|
+
import { customFieldDefinitions, customFieldValues } from "../schema.js";
|
|
3
|
+
import { paginate } from "./helpers.js";
|
|
4
|
+
export const customFieldsService = {
|
|
5
|
+
async listCustomFieldDefinitions(db, query) {
|
|
6
|
+
const where = query.entityType
|
|
7
|
+
? eq(customFieldDefinitions.entityType, query.entityType)
|
|
8
|
+
: undefined;
|
|
9
|
+
return paginate(db
|
|
10
|
+
.select()
|
|
11
|
+
.from(customFieldDefinitions)
|
|
12
|
+
.where(where)
|
|
13
|
+
.limit(query.limit)
|
|
14
|
+
.offset(query.offset)
|
|
15
|
+
.orderBy(customFieldDefinitions.entityType, customFieldDefinitions.label), db.select({ count: sql `count(*)::int` }).from(customFieldDefinitions).where(where), query.limit, query.offset);
|
|
16
|
+
},
|
|
17
|
+
async getCustomFieldDefinitionById(db, id) {
|
|
18
|
+
const [row] = await db
|
|
19
|
+
.select()
|
|
20
|
+
.from(customFieldDefinitions)
|
|
21
|
+
.where(eq(customFieldDefinitions.id, id))
|
|
22
|
+
.limit(1);
|
|
23
|
+
return row ?? null;
|
|
24
|
+
},
|
|
25
|
+
async createCustomFieldDefinition(db, data) {
|
|
26
|
+
const [row] = await db.insert(customFieldDefinitions).values(data).returning();
|
|
27
|
+
return row;
|
|
28
|
+
},
|
|
29
|
+
async updateCustomFieldDefinition(db, id, data) {
|
|
30
|
+
const [row] = await db
|
|
31
|
+
.update(customFieldDefinitions)
|
|
32
|
+
.set({ ...data, updatedAt: new Date() })
|
|
33
|
+
.where(eq(customFieldDefinitions.id, id))
|
|
34
|
+
.returning();
|
|
35
|
+
return row ?? null;
|
|
36
|
+
},
|
|
37
|
+
async deleteCustomFieldDefinition(db, id) {
|
|
38
|
+
const [row] = await db
|
|
39
|
+
.delete(customFieldDefinitions)
|
|
40
|
+
.where(eq(customFieldDefinitions.id, id))
|
|
41
|
+
.returning({ id: customFieldDefinitions.id });
|
|
42
|
+
return row ?? null;
|
|
43
|
+
},
|
|
44
|
+
async listCustomFieldValues(db, query) {
|
|
45
|
+
const conditions = [];
|
|
46
|
+
if (query.entityType)
|
|
47
|
+
conditions.push(eq(customFieldValues.entityType, query.entityType));
|
|
48
|
+
if (query.entityId)
|
|
49
|
+
conditions.push(eq(customFieldValues.entityId, query.entityId));
|
|
50
|
+
if (query.definitionId)
|
|
51
|
+
conditions.push(eq(customFieldValues.definitionId, query.definitionId));
|
|
52
|
+
const where = conditions.length ? and(...conditions) : undefined;
|
|
53
|
+
return paginate(db
|
|
54
|
+
.select()
|
|
55
|
+
.from(customFieldValues)
|
|
56
|
+
.where(where)
|
|
57
|
+
.limit(query.limit)
|
|
58
|
+
.offset(query.offset)
|
|
59
|
+
.orderBy(desc(customFieldValues.updatedAt)), db.select({ count: sql `count(*)::int` }).from(customFieldValues).where(where), query.limit, query.offset);
|
|
60
|
+
},
|
|
61
|
+
async upsertCustomFieldValue(db, definitionId, data) {
|
|
62
|
+
const [existing] = await db
|
|
63
|
+
.select()
|
|
64
|
+
.from(customFieldValues)
|
|
65
|
+
.where(and(eq(customFieldValues.definitionId, definitionId), eq(customFieldValues.entityType, data.entityType), eq(customFieldValues.entityId, data.entityId)))
|
|
66
|
+
.limit(1);
|
|
67
|
+
if (existing) {
|
|
68
|
+
const [row] = await db
|
|
69
|
+
.update(customFieldValues)
|
|
70
|
+
.set({ ...data, definitionId, updatedAt: new Date() })
|
|
71
|
+
.where(eq(customFieldValues.id, existing.id))
|
|
72
|
+
.returning();
|
|
73
|
+
return row;
|
|
74
|
+
}
|
|
75
|
+
const [row] = await db
|
|
76
|
+
.insert(customFieldValues)
|
|
77
|
+
.values({ ...data, definitionId })
|
|
78
|
+
.returning();
|
|
79
|
+
return row;
|
|
80
|
+
},
|
|
81
|
+
async deleteCustomFieldValue(db, id) {
|
|
82
|
+
const [row] = await db
|
|
83
|
+
.delete(customFieldValues)
|
|
84
|
+
.where(eq(customFieldValues.id, id))
|
|
85
|
+
.returning({ id: customFieldValues.id });
|
|
86
|
+
return row ?? null;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare function paginate<T extends object>(rowsQuery: Promise<T[]>, countQuery: Promise<Array<{
|
|
2
|
+
count: number;
|
|
3
|
+
}>>, limit: number, offset: number): Promise<{
|
|
4
|
+
data: T[];
|
|
5
|
+
total: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
offset: number;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function toDateOrNull(value: string | null | undefined): Date | null;
|
|
10
|
+
export declare function normalizeContactValue(kind: "email" | "phone" | "website", value: string): string;
|
|
11
|
+
export declare function isManagedBySource(metadata: Record<string, unknown> | null | undefined, source: string): boolean;
|
|
12
|
+
export declare function toNullableTrimmed(value: string | null | undefined): string | null;
|
|
13
|
+
export declare function formatAddress(address: {
|
|
14
|
+
fullText: string | null;
|
|
15
|
+
line1: string | null;
|
|
16
|
+
line2: string | null;
|
|
17
|
+
city: string | null;
|
|
18
|
+
region: string | null;
|
|
19
|
+
postalCode: string | null;
|
|
20
|
+
country: string | null;
|
|
21
|
+
}): string | null;
|
|
22
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/service/helpers.ts"],"names":[],"mappings":"AAAA,wBAAsB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAC7C,SAAS,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EACvB,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,EAC7C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM;;;;;GAUf;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,eAE5D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,UAKvF;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EACpD,MAAM,EAAE,MAAM,WAGf;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,iBAGjE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,iBAaA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export async function paginate(rowsQuery, countQuery, limit, offset) {
|
|
2
|
+
const [data, countResult] = await Promise.all([rowsQuery, countQuery]);
|
|
3
|
+
return {
|
|
4
|
+
data,
|
|
5
|
+
total: countResult[0]?.count ?? 0,
|
|
6
|
+
limit,
|
|
7
|
+
offset,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function toDateOrNull(value) {
|
|
11
|
+
return value ? new Date(value) : null;
|
|
12
|
+
}
|
|
13
|
+
export function normalizeContactValue(kind, value) {
|
|
14
|
+
if (kind === "email" || kind === "website") {
|
|
15
|
+
return value.trim().toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
return value.trim();
|
|
18
|
+
}
|
|
19
|
+
export function isManagedBySource(metadata, source) {
|
|
20
|
+
return metadata?.managedBy === source;
|
|
21
|
+
}
|
|
22
|
+
export function toNullableTrimmed(value) {
|
|
23
|
+
const trimmed = value?.trim();
|
|
24
|
+
return trimmed ? trimmed : null;
|
|
25
|
+
}
|
|
26
|
+
export function formatAddress(address) {
|
|
27
|
+
if (address.fullText) {
|
|
28
|
+
return address.fullText;
|
|
29
|
+
}
|
|
30
|
+
const parts = [
|
|
31
|
+
address.line1,
|
|
32
|
+
address.line2,
|
|
33
|
+
address.city,
|
|
34
|
+
address.region,
|
|
35
|
+
address.postalCode,
|
|
36
|
+
address.country,
|
|
37
|
+
].filter(Boolean);
|
|
38
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
39
|
+
}
|