@ikatec/digisac-api-sdk 3.0.0 → 3.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/dist/apis/cards/types.d.ts +89 -7
- package/dist/apis/cards/types.d.ts.map +1 -1
- package/dist/apis/pipeline/types.d.ts +67 -3
- package/dist/apis/pipeline/types.d.ts.map +1 -1
- package/dist/core/BaseApiClient.cjs +26 -1
- package/dist/core/BaseApiClient.cjs.map +1 -1
- package/dist/core/BaseApiClient.d.ts.map +1 -1
- package/dist/core/BaseApiClient.mjs +26 -1
- package/dist/core/BaseApiClient.mjs.map +1 -1
- package/dist/incommingWebhooks/index.d.ts +3 -3
- package/dist/incommingWebhooks/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/apis/cards/types.ts +104 -7
- package/src/apis/pipeline/types.ts +71 -3
- package/src/core/BaseApiClient.test.ts +53 -0
- package/src/core/BaseApiClient.ts +37 -1
- package/src/incommingWebhooks/index.ts +3 -3
|
@@ -1,13 +1,71 @@
|
|
|
1
1
|
import type { Contact } from '../contacts/types';
|
|
2
|
-
import type { Pipeline } from '../pipeline/types';
|
|
2
|
+
import type { Pipeline, PipelineStage, PipelineStageReason, PipelineStageStatus } from '../pipeline/types';
|
|
3
3
|
import type { User } from '../users/types';
|
|
4
|
+
type NamedRef = {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
/** One entry of the card history — written on create and on every stage/status/owner change. */
|
|
9
|
+
export type CardMovement = {
|
|
10
|
+
id: string;
|
|
11
|
+
cardId: string;
|
|
12
|
+
accountId: string;
|
|
13
|
+
/** The user who made the change (the requester), not the card owner. */
|
|
14
|
+
userId: string;
|
|
15
|
+
pipelineId: string;
|
|
16
|
+
fromPipelineId: string | null;
|
|
17
|
+
toPipelineId: string | null;
|
|
18
|
+
fromPipelineStageId: string | null;
|
|
19
|
+
toPipelineStageId: string | null;
|
|
20
|
+
fromStageStatusId: string | null;
|
|
21
|
+
toStageStatusId: string | null;
|
|
22
|
+
fromOwnerId: string | null;
|
|
23
|
+
toOwnerId: string | null;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
user?: NamedRef | null;
|
|
26
|
+
from_pipeline?: NamedRef | null;
|
|
27
|
+
to_pipeline?: NamedRef | null;
|
|
28
|
+
from_pipeline_stage?: NamedRef | null;
|
|
29
|
+
to_pipeline_stage?: NamedRef | null;
|
|
30
|
+
from_stage_status?: NamedRef | null;
|
|
31
|
+
to_stage_status?: NamedRef | null;
|
|
32
|
+
from_owner?: NamedRef | null;
|
|
33
|
+
to_owner?: NamedRef | null;
|
|
34
|
+
};
|
|
35
|
+
export type CardProduct = {
|
|
36
|
+
id: string;
|
|
37
|
+
cardId: string;
|
|
38
|
+
name: string;
|
|
39
|
+
/** Quantity (the backend column is spelled `ammount`). */
|
|
40
|
+
ammount: number;
|
|
41
|
+
value: number;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
updatedAt: string;
|
|
44
|
+
};
|
|
45
|
+
export type CardComment = {
|
|
46
|
+
id: string;
|
|
47
|
+
cardId: string;
|
|
48
|
+
userId: string | null;
|
|
49
|
+
comment: string | null;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
updatedAt: string;
|
|
52
|
+
user?: User | null;
|
|
53
|
+
files?: Record<string, unknown>[];
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* A sales funnel card (opportunity).
|
|
57
|
+
*
|
|
58
|
+
* `create` and `updateById` answer the raw entity (with `updatedAt`/`archivedAt`). `getMany` and
|
|
59
|
+
* `getById` go through the backend transformer: no `updatedAt`/`archivedAt`, plus `url`,
|
|
60
|
+
* `lastMessage` and `totalValue`; `getById` also joins the relationships below.
|
|
61
|
+
*/
|
|
4
62
|
export type Card = {
|
|
5
63
|
id: string;
|
|
6
64
|
title: string;
|
|
7
65
|
description: string | null;
|
|
66
|
+
/** Position in the stage — set by the backend to the number of cards already in it. */
|
|
8
67
|
order: number | null;
|
|
9
68
|
isArchived: boolean;
|
|
10
|
-
archivedAt: string | null;
|
|
11
69
|
success: boolean | null;
|
|
12
70
|
finishedAt: string | null;
|
|
13
71
|
organization: string | null;
|
|
@@ -22,38 +80,62 @@ export type Card = {
|
|
|
22
80
|
reasonId: string | null;
|
|
23
81
|
ownerId: string | null;
|
|
24
82
|
createdAt: string;
|
|
25
|
-
|
|
83
|
+
/** Only on create/update answers. */
|
|
84
|
+
updatedAt?: string;
|
|
85
|
+
/** Only on create/update answers. */
|
|
86
|
+
archivedAt?: string | null;
|
|
87
|
+
/** Only on getMany/getById answers (contact avatar URL, or empty string). */
|
|
88
|
+
url?: unknown;
|
|
89
|
+
/** Only on getMany/getById answers (contact last message, or empty string). */
|
|
90
|
+
lastMessage?: unknown;
|
|
91
|
+
/** Only on getMany/getById answers: sum of the products (`ammount * value`). */
|
|
92
|
+
totalValue?: number;
|
|
26
93
|
contact?: Contact | null;
|
|
27
94
|
owner?: User | null;
|
|
28
95
|
pipeline?: Pipeline;
|
|
96
|
+
pipeline_stage?: PipelineStage | null;
|
|
97
|
+
stage_status?: PipelineStageStatus | null;
|
|
98
|
+
stage_reason?: PipelineStageReason | null;
|
|
99
|
+
movements?: CardMovement[];
|
|
100
|
+
products?: CardProduct[];
|
|
101
|
+
comments?: CardComment[];
|
|
29
102
|
};
|
|
30
|
-
export type CardRelationships = 'contact' | 'owner' | 'pipeline';
|
|
103
|
+
export type CardRelationships = 'contact' | 'owner' | 'pipeline' | 'pipeline_stage' | 'stage_status' | 'stage_reason' | 'movements' | 'products' | 'comments';
|
|
31
104
|
export type CreateCardPayload = {
|
|
105
|
+
/** Up to 255 characters. */
|
|
32
106
|
title: string;
|
|
33
107
|
pipelineId: string;
|
|
34
|
-
contactId
|
|
35
|
-
|
|
108
|
+
contactId: string;
|
|
109
|
+
/** Must be a stage of `pipelineId`. */
|
|
110
|
+
pipelineStageId: string;
|
|
36
111
|
description?: string;
|
|
112
|
+
ownerId?: string;
|
|
113
|
+
statusId?: string;
|
|
114
|
+
reasonId?: string;
|
|
37
115
|
organization?: string;
|
|
38
116
|
organizationSegment?: string;
|
|
39
117
|
originChannel?: string;
|
|
40
118
|
originCampaign?: string;
|
|
41
|
-
ownerId?: string;
|
|
42
119
|
};
|
|
43
120
|
export type UpdateCardPayload = {
|
|
44
121
|
title?: string;
|
|
45
122
|
description?: string | null;
|
|
46
123
|
contactId?: string | null;
|
|
124
|
+
/** Moves the card to another pipeline (send the target `pipelineStageId` along). */
|
|
125
|
+
pipelineId?: string;
|
|
47
126
|
pipelineStageId?: string | null;
|
|
48
127
|
statusId?: string | null;
|
|
49
128
|
reasonId?: string | null;
|
|
50
129
|
ownerId?: string | null;
|
|
130
|
+
order?: number;
|
|
51
131
|
organization?: string | null;
|
|
52
132
|
organizationSegment?: string | null;
|
|
53
133
|
originChannel?: string | null;
|
|
54
134
|
originCampaign?: string | null;
|
|
55
135
|
isArchived?: boolean;
|
|
136
|
+
/** Marks the card as won (`true`) or lost (`false`); the backend fills `finishedAt`. */
|
|
56
137
|
success?: boolean | null;
|
|
57
138
|
finishedAt?: string | null;
|
|
58
139
|
};
|
|
140
|
+
export {};
|
|
59
141
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/cards/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/cards/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
import type { Department } from '../departments/types';
|
|
2
|
-
export type
|
|
2
|
+
export type PipelineStageStatus = {
|
|
3
|
+
id: string;
|
|
4
|
+
/** The default "Finalizados" stage carries `STATUS_WON` and `STATUS_LOOSE`. */
|
|
5
|
+
name: string;
|
|
6
|
+
position: number;
|
|
7
|
+
pipelineId: string;
|
|
8
|
+
stageId: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
deletedAt: string | null;
|
|
13
|
+
};
|
|
14
|
+
export type PipelineStageReason = {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
position: number;
|
|
18
|
+
isWon: boolean;
|
|
19
|
+
pipelineId: string;
|
|
20
|
+
stageId: string;
|
|
21
|
+
accountId: string;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
updatedAt: string;
|
|
24
|
+
};
|
|
25
|
+
export type PipelineStage = {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
position: number;
|
|
29
|
+
pipelineId: string;
|
|
30
|
+
accountId: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
deletedAt: string | null;
|
|
34
|
+
statuses?: PipelineStageStatus[];
|
|
35
|
+
reasons?: PipelineStageReason[];
|
|
36
|
+
/** Card totals — present on create/update/getById, absent on the listing `include`. */
|
|
37
|
+
totalCards?: number;
|
|
38
|
+
totalCardsValue?: number;
|
|
39
|
+
totalWonCardsValue?: number;
|
|
40
|
+
totalLostCardsValue?: number;
|
|
41
|
+
};
|
|
42
|
+
/** The pipeline row as stored — what the webhook events (`pipeline.created`/`.updated`) carry. */
|
|
43
|
+
export type PipelineRecord = {
|
|
3
44
|
id: string;
|
|
4
45
|
name: string;
|
|
5
46
|
goBack: boolean;
|
|
@@ -9,16 +50,39 @@ export type Pipeline = {
|
|
|
9
50
|
createdAt: string;
|
|
10
51
|
updatedAt: string;
|
|
11
52
|
deletedAt: string | null;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* A sales funnel pipeline.
|
|
56
|
+
*
|
|
57
|
+
* `create`, `updateById` and `getById` answer the full entity, always with `stages`. The listing
|
|
58
|
+
* (`getMany`) goes through the backend transformer and only carries `id`, `name`, `goBack` and
|
|
59
|
+
* `archivedAt` (plus the included relationships) — hence the optional fields below.
|
|
60
|
+
*/
|
|
61
|
+
export type Pipeline = {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
goBack: boolean;
|
|
65
|
+
archivedAt: string | null;
|
|
66
|
+
accountId?: string;
|
|
67
|
+
permissionToViewCard?: boolean | null;
|
|
68
|
+
createdAt?: string;
|
|
69
|
+
updatedAt?: string;
|
|
70
|
+
deletedAt?: string | null;
|
|
71
|
+
stages?: PipelineStage[];
|
|
12
72
|
departments?: Department[];
|
|
13
73
|
};
|
|
14
|
-
export type PipelineRelationships = 'departments';
|
|
74
|
+
export type PipelineRelationships = 'departments' | 'stages';
|
|
15
75
|
export type CreatePipelinePayload = {
|
|
76
|
+
/** Up to 100 characters; must be unique in the account. */
|
|
16
77
|
name: string;
|
|
78
|
+
/** `false` = only move forward, `true` = move forward and backward. */
|
|
17
79
|
goBack?: boolean;
|
|
80
|
+
/** When `true`, non-admin users only see the cards they own. */
|
|
18
81
|
permissionToViewCard?: boolean;
|
|
19
82
|
};
|
|
20
83
|
export type UpdatePipelinePayload = {
|
|
21
|
-
|
|
84
|
+
/** Required by the backend on every update (400 `required` otherwise). */
|
|
85
|
+
name: string;
|
|
22
86
|
goBack?: boolean;
|
|
23
87
|
permissionToViewCard?: boolean | null;
|
|
24
88
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/pipeline/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,MAAM,QAAQ,GAAG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/pipeline/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
//#region src/core/BaseApiClient.ts
|
|
4
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
/**
|
|
6
|
+
* Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with
|
|
7
|
+
* `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).
|
|
8
|
+
* Returns `null` when the body carries no field errors.
|
|
9
|
+
*/
|
|
10
|
+
function parseValidationErrors(errors) {
|
|
11
|
+
if (!isRecord(errors)) return null;
|
|
12
|
+
const parsed = [];
|
|
13
|
+
for (const [location, fields] of Object.entries(errors)) {
|
|
14
|
+
if (!isRecord(fields)) continue;
|
|
15
|
+
for (const [field, detail] of Object.entries(fields)) {
|
|
16
|
+
const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : [];
|
|
17
|
+
const types = Array.isArray(detail?.types) ? detail.types.map(String) : [];
|
|
18
|
+
messages.forEach((message, index) => {
|
|
19
|
+
parsed.push({
|
|
20
|
+
path: `${location}.${field}`,
|
|
21
|
+
message,
|
|
22
|
+
code: types[index] ?? types[0] ?? ""
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return parsed.length ? parsed : null;
|
|
28
|
+
}
|
|
4
29
|
var ApiError = class extends Error {
|
|
5
30
|
errorClass;
|
|
6
31
|
status;
|
|
@@ -49,7 +74,7 @@ var BaseApiClient = class {
|
|
|
49
74
|
const errorData = JSON.parse(errorText);
|
|
50
75
|
errorMessage = errorData.message || errorData.error || errorMessage;
|
|
51
76
|
errorClass = errorData.error;
|
|
52
|
-
validationErrors = errorData.validationErrors ??
|
|
77
|
+
validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors);
|
|
53
78
|
}
|
|
54
79
|
} catch {}
|
|
55
80
|
throw new ApiError(errorMessage, errorClass, response.status, validationErrors);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseApiClient.cjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ??
|
|
1
|
+
{"version":3,"file":"BaseApiClient.cjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\n/**\n * Digisac field errors: `{ errors: { <location>: { <field>: { messages: string[], types: string[] } } } }`,\n * where location is `body`, `params`, `query`, `headers` or `cookies` (backend `validate` middleware).\n */\ntype DigisacFieldErrors = Record<string, Record<string, { messages?: unknown; types?: unknown }>>\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with\n * `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).\n * Returns `null` when the body carries no field errors.\n */\nfunction parseValidationErrors(errors: unknown): ValidationError[] | null {\n if (!isRecord(errors)) {\n return null\n }\n\n const parsed: ValidationError[] = []\n for (const [location, fields] of Object.entries(errors as DigisacFieldErrors)) {\n if (!isRecord(fields)) {\n continue\n }\n for (const [field, detail] of Object.entries(fields)) {\n const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : []\n const types = Array.isArray(detail?.types) ? detail.types.map(String) : []\n messages.forEach((message, index) => {\n parsed.push({ path: `${location}.${field}`, message, code: types[index] ?? types[0] ?? '' })\n })\n }\n }\n\n return parsed.length ? parsed : null\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors)\n }\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;
|
|
1
|
+
{"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAsCD,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/C,SAAgB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C,SAAgB,gBAAgB,EAAE,eAAe,EAAE,GAAG,IAAI,CAAA;gBAGxD,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,gBAAgB,GAAE,eAAe,EAAE,GAAG,IAAW;CAQpD;AAED,qBAAa,aAAc,YAAW,SAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAClC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;gBAE/B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAKjD,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EAChD,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;cAgBL,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAuDjF,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAIhG,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,KAAK,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,MAAM,CAAC,SAAS,GAAG,OAAO,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;CAGtB"}
|
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
//#region src/core/BaseApiClient.ts
|
|
2
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
/**
|
|
4
|
+
* Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with
|
|
5
|
+
* `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).
|
|
6
|
+
* Returns `null` when the body carries no field errors.
|
|
7
|
+
*/
|
|
8
|
+
function parseValidationErrors(errors) {
|
|
9
|
+
if (!isRecord(errors)) return null;
|
|
10
|
+
const parsed = [];
|
|
11
|
+
for (const [location, fields] of Object.entries(errors)) {
|
|
12
|
+
if (!isRecord(fields)) continue;
|
|
13
|
+
for (const [field, detail] of Object.entries(fields)) {
|
|
14
|
+
const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : [];
|
|
15
|
+
const types = Array.isArray(detail?.types) ? detail.types.map(String) : [];
|
|
16
|
+
messages.forEach((message, index) => {
|
|
17
|
+
parsed.push({
|
|
18
|
+
path: `${location}.${field}`,
|
|
19
|
+
message,
|
|
20
|
+
code: types[index] ?? types[0] ?? ""
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return parsed.length ? parsed : null;
|
|
26
|
+
}
|
|
2
27
|
var ApiError = class extends Error {
|
|
3
28
|
errorClass;
|
|
4
29
|
status;
|
|
@@ -47,7 +72,7 @@ var BaseApiClient = class {
|
|
|
47
72
|
const errorData = JSON.parse(errorText);
|
|
48
73
|
errorMessage = errorData.message || errorData.error || errorMessage;
|
|
49
74
|
errorClass = errorData.error;
|
|
50
|
-
validationErrors = errorData.validationErrors ??
|
|
75
|
+
validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors);
|
|
51
76
|
}
|
|
52
77
|
} catch {}
|
|
53
78
|
throw new ApiError(errorMessage, errorClass, response.status, validationErrors);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ??
|
|
1
|
+
{"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\n/**\n * Digisac field errors: `{ errors: { <location>: { <field>: { messages: string[], types: string[] } } } }`,\n * where location is `body`, `params`, `query`, `headers` or `cookies` (backend `validate` middleware).\n */\ntype DigisacFieldErrors = Record<string, Record<string, { messages?: unknown; types?: unknown }>>\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with\n * `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).\n * Returns `null` when the body carries no field errors.\n */\nfunction parseValidationErrors(errors: unknown): ValidationError[] | null {\n if (!isRecord(errors)) {\n return null\n }\n\n const parsed: ValidationError[] = []\n for (const [location, fields] of Object.entries(errors as DigisacFieldErrors)) {\n if (!isRecord(fields)) {\n continue\n }\n for (const [field, detail] of Object.entries(fields)) {\n const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : []\n const types = Array.isArray(detail?.types) ? detail.types.map(String) : []\n messages.forEach((message, index) => {\n parsed.push({ path: `${location}.${field}`, message, code: types[index] ?? types[0] ?? '' })\n })\n }\n }\n\n return parsed.length ? parsed : null\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors)\n }\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import type { Department, DepartmentRelationships } from '../apis/departments';
|
|
|
5
5
|
import type { Message, MessageRelationships } from '../apis/messages';
|
|
6
6
|
import type { Organization, OrganizationRelationships } from '../apis/organizations';
|
|
7
7
|
import type { Person, PersonRelationships } from '../apis/people';
|
|
8
|
-
import type {
|
|
8
|
+
import type { PipelineRecord } from '../apis/pipeline';
|
|
9
9
|
import type { QuickReply, QuickReplyRelationships } from '../apis/quickReplies';
|
|
10
10
|
import type { Role, RoleRelationships } from '../apis/roles';
|
|
11
11
|
import type { Service, ServiceRelationships } from '../apis/services';
|
|
@@ -66,8 +66,8 @@ export interface WebhookEventPayloadMap {
|
|
|
66
66
|
'campaign.created': Omit<Campaign, CampaignRelationships>;
|
|
67
67
|
'campaign.updated': Omit<Campaign, CampaignRelationships>;
|
|
68
68
|
'campaign.destroyed': Omit<Campaign, CampaignRelationships>;
|
|
69
|
-
'pipeline.created':
|
|
70
|
-
'pipeline.updated':
|
|
69
|
+
'pipeline.created': PipelineRecord;
|
|
70
|
+
'pipeline.updated': PipelineRecord;
|
|
71
71
|
}
|
|
72
72
|
export type WebhookPayload<E extends WebhookEvent = WebhookEvent> = {
|
|
73
73
|
event: E;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/incommingWebhooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,KAAK,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AACpF,OAAO,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACjE,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/incommingWebhooks/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/apis/cards/types.ts
CHANGED
|
@@ -1,14 +1,79 @@
|
|
|
1
1
|
import type { Contact } from '../contacts/types'
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
Pipeline,
|
|
4
|
+
PipelineStage,
|
|
5
|
+
PipelineStageReason,
|
|
6
|
+
PipelineStageStatus,
|
|
7
|
+
} from '../pipeline/types'
|
|
3
8
|
import type { User } from '../users/types'
|
|
4
9
|
|
|
10
|
+
type NamedRef = { id: string; name: string }
|
|
11
|
+
|
|
12
|
+
/** One entry of the card history — written on create and on every stage/status/owner change. */
|
|
13
|
+
export type CardMovement = {
|
|
14
|
+
id: string
|
|
15
|
+
cardId: string
|
|
16
|
+
accountId: string
|
|
17
|
+
/** The user who made the change (the requester), not the card owner. */
|
|
18
|
+
userId: string
|
|
19
|
+
pipelineId: string
|
|
20
|
+
fromPipelineId: string | null
|
|
21
|
+
toPipelineId: string | null
|
|
22
|
+
fromPipelineStageId: string | null
|
|
23
|
+
toPipelineStageId: string | null
|
|
24
|
+
fromStageStatusId: string | null
|
|
25
|
+
toStageStatusId: string | null
|
|
26
|
+
fromOwnerId: string | null
|
|
27
|
+
toOwnerId: string | null
|
|
28
|
+
createdAt: string
|
|
29
|
+
// relationships (GET /cards/{id})
|
|
30
|
+
user?: NamedRef | null
|
|
31
|
+
from_pipeline?: NamedRef | null
|
|
32
|
+
to_pipeline?: NamedRef | null
|
|
33
|
+
from_pipeline_stage?: NamedRef | null
|
|
34
|
+
to_pipeline_stage?: NamedRef | null
|
|
35
|
+
from_stage_status?: NamedRef | null
|
|
36
|
+
to_stage_status?: NamedRef | null
|
|
37
|
+
from_owner?: NamedRef | null
|
|
38
|
+
to_owner?: NamedRef | null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type CardProduct = {
|
|
42
|
+
id: string
|
|
43
|
+
cardId: string
|
|
44
|
+
name: string
|
|
45
|
+
/** Quantity (the backend column is spelled `ammount`). */
|
|
46
|
+
ammount: number
|
|
47
|
+
value: number
|
|
48
|
+
createdAt: string
|
|
49
|
+
updatedAt: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type CardComment = {
|
|
53
|
+
id: string
|
|
54
|
+
cardId: string
|
|
55
|
+
userId: string | null
|
|
56
|
+
comment: string | null
|
|
57
|
+
createdAt: string
|
|
58
|
+
updatedAt: string
|
|
59
|
+
user?: User | null
|
|
60
|
+
files?: Record<string, unknown>[]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A sales funnel card (opportunity).
|
|
65
|
+
*
|
|
66
|
+
* `create` and `updateById` answer the raw entity (with `updatedAt`/`archivedAt`). `getMany` and
|
|
67
|
+
* `getById` go through the backend transformer: no `updatedAt`/`archivedAt`, plus `url`,
|
|
68
|
+
* `lastMessage` and `totalValue`; `getById` also joins the relationships below.
|
|
69
|
+
*/
|
|
5
70
|
export type Card = {
|
|
6
71
|
id: string
|
|
7
72
|
title: string
|
|
8
73
|
description: string | null
|
|
74
|
+
/** Position in the stage — set by the backend to the number of cards already in it. */
|
|
9
75
|
order: number | null
|
|
10
76
|
isArchived: boolean
|
|
11
|
-
archivedAt: string | null
|
|
12
77
|
success: boolean | null
|
|
13
78
|
finishedAt: string | null
|
|
14
79
|
organization: string | null
|
|
@@ -23,41 +88,73 @@ export type Card = {
|
|
|
23
88
|
reasonId: string | null
|
|
24
89
|
ownerId: string | null
|
|
25
90
|
createdAt: string
|
|
26
|
-
|
|
91
|
+
/** Only on create/update answers. */
|
|
92
|
+
updatedAt?: string
|
|
93
|
+
/** Only on create/update answers. */
|
|
94
|
+
archivedAt?: string | null
|
|
95
|
+
/** Only on getMany/getById answers (contact avatar URL, or empty string). */
|
|
96
|
+
url?: unknown
|
|
97
|
+
/** Only on getMany/getById answers (contact last message, or empty string). */
|
|
98
|
+
lastMessage?: unknown
|
|
99
|
+
/** Only on getMany/getById answers: sum of the products (`ammount * value`). */
|
|
100
|
+
totalValue?: number
|
|
27
101
|
// relationships
|
|
28
102
|
contact?: Contact | null
|
|
29
103
|
owner?: User | null
|
|
30
104
|
pipeline?: Pipeline
|
|
105
|
+
pipeline_stage?: PipelineStage | null
|
|
106
|
+
stage_status?: PipelineStageStatus | null
|
|
107
|
+
stage_reason?: PipelineStageReason | null
|
|
108
|
+
movements?: CardMovement[]
|
|
109
|
+
products?: CardProduct[]
|
|
110
|
+
comments?: CardComment[]
|
|
31
111
|
}
|
|
32
112
|
|
|
33
|
-
export type CardRelationships =
|
|
113
|
+
export type CardRelationships =
|
|
114
|
+
| 'contact'
|
|
115
|
+
| 'owner'
|
|
116
|
+
| 'pipeline'
|
|
117
|
+
| 'pipeline_stage'
|
|
118
|
+
| 'stage_status'
|
|
119
|
+
| 'stage_reason'
|
|
120
|
+
| 'movements'
|
|
121
|
+
| 'products'
|
|
122
|
+
| 'comments'
|
|
34
123
|
|
|
35
124
|
export type CreateCardPayload = {
|
|
125
|
+
/** Up to 255 characters. */
|
|
36
126
|
title: string
|
|
37
127
|
pipelineId: string
|
|
38
|
-
contactId
|
|
39
|
-
|
|
128
|
+
contactId: string
|
|
129
|
+
/** Must be a stage of `pipelineId`. */
|
|
130
|
+
pipelineStageId: string
|
|
40
131
|
description?: string
|
|
132
|
+
ownerId?: string
|
|
133
|
+
statusId?: string
|
|
134
|
+
reasonId?: string
|
|
41
135
|
organization?: string
|
|
42
136
|
organizationSegment?: string
|
|
43
137
|
originChannel?: string
|
|
44
138
|
originCampaign?: string
|
|
45
|
-
ownerId?: string
|
|
46
139
|
}
|
|
47
140
|
|
|
48
141
|
export type UpdateCardPayload = {
|
|
49
142
|
title?: string
|
|
50
143
|
description?: string | null
|
|
51
144
|
contactId?: string | null
|
|
145
|
+
/** Moves the card to another pipeline (send the target `pipelineStageId` along). */
|
|
146
|
+
pipelineId?: string
|
|
52
147
|
pipelineStageId?: string | null
|
|
53
148
|
statusId?: string | null
|
|
54
149
|
reasonId?: string | null
|
|
55
150
|
ownerId?: string | null
|
|
151
|
+
order?: number
|
|
56
152
|
organization?: string | null
|
|
57
153
|
organizationSegment?: string | null
|
|
58
154
|
originChannel?: string | null
|
|
59
155
|
originCampaign?: string | null
|
|
60
156
|
isArchived?: boolean
|
|
157
|
+
/** Marks the card as won (`true`) or lost (`false`); the backend fills `finishedAt`. */
|
|
61
158
|
success?: boolean | null
|
|
62
159
|
finishedAt?: string | null
|
|
63
160
|
}
|
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
import type { Department } from '../departments/types'
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type PipelineStageStatus = {
|
|
4
|
+
id: string
|
|
5
|
+
/** The default "Finalizados" stage carries `STATUS_WON` and `STATUS_LOOSE`. */
|
|
6
|
+
name: string
|
|
7
|
+
position: number
|
|
8
|
+
pipelineId: string
|
|
9
|
+
stageId: string
|
|
10
|
+
accountId: string
|
|
11
|
+
createdAt: string
|
|
12
|
+
updatedAt: string
|
|
13
|
+
deletedAt: string | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type PipelineStageReason = {
|
|
17
|
+
id: string
|
|
18
|
+
name: string
|
|
19
|
+
position: number
|
|
20
|
+
isWon: boolean
|
|
21
|
+
pipelineId: string
|
|
22
|
+
stageId: string
|
|
23
|
+
accountId: string
|
|
24
|
+
createdAt: string
|
|
25
|
+
updatedAt: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type PipelineStage = {
|
|
29
|
+
id: string
|
|
30
|
+
name: string
|
|
31
|
+
position: number
|
|
32
|
+
pipelineId: string
|
|
33
|
+
accountId: string
|
|
34
|
+
createdAt: string
|
|
35
|
+
updatedAt: string
|
|
36
|
+
deletedAt: string | null
|
|
37
|
+
statuses?: PipelineStageStatus[]
|
|
38
|
+
reasons?: PipelineStageReason[]
|
|
39
|
+
/** Card totals — present on create/update/getById, absent on the listing `include`. */
|
|
40
|
+
totalCards?: number
|
|
41
|
+
totalCardsValue?: number
|
|
42
|
+
totalWonCardsValue?: number
|
|
43
|
+
totalLostCardsValue?: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The pipeline row as stored — what the webhook events (`pipeline.created`/`.updated`) carry. */
|
|
47
|
+
export type PipelineRecord = {
|
|
4
48
|
id: string
|
|
5
49
|
name: string
|
|
6
50
|
goBack: boolean
|
|
@@ -10,20 +54,44 @@ export type Pipeline = {
|
|
|
10
54
|
createdAt: string
|
|
11
55
|
updatedAt: string
|
|
12
56
|
deletedAt: string | null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A sales funnel pipeline.
|
|
61
|
+
*
|
|
62
|
+
* `create`, `updateById` and `getById` answer the full entity, always with `stages`. The listing
|
|
63
|
+
* (`getMany`) goes through the backend transformer and only carries `id`, `name`, `goBack` and
|
|
64
|
+
* `archivedAt` (plus the included relationships) — hence the optional fields below.
|
|
65
|
+
*/
|
|
66
|
+
export type Pipeline = {
|
|
67
|
+
id: string
|
|
68
|
+
name: string
|
|
69
|
+
goBack: boolean
|
|
70
|
+
archivedAt: string | null
|
|
71
|
+
accountId?: string
|
|
72
|
+
permissionToViewCard?: boolean | null
|
|
73
|
+
createdAt?: string
|
|
74
|
+
updatedAt?: string
|
|
75
|
+
deletedAt?: string | null
|
|
13
76
|
// relationships
|
|
77
|
+
stages?: PipelineStage[]
|
|
14
78
|
departments?: Department[]
|
|
15
79
|
}
|
|
16
80
|
|
|
17
|
-
export type PipelineRelationships = 'departments'
|
|
81
|
+
export type PipelineRelationships = 'departments' | 'stages'
|
|
18
82
|
|
|
19
83
|
export type CreatePipelinePayload = {
|
|
84
|
+
/** Up to 100 characters; must be unique in the account. */
|
|
20
85
|
name: string
|
|
86
|
+
/** `false` = only move forward, `true` = move forward and backward. */
|
|
21
87
|
goBack?: boolean
|
|
88
|
+
/** When `true`, non-admin users only see the cards they own. */
|
|
22
89
|
permissionToViewCard?: boolean
|
|
23
90
|
}
|
|
24
91
|
|
|
25
92
|
export type UpdatePipelinePayload = {
|
|
26
|
-
|
|
93
|
+
/** Required by the backend on every update (400 `required` otherwise). */
|
|
94
|
+
name: string
|
|
27
95
|
goBack?: boolean
|
|
28
96
|
permissionToViewCard?: boolean | null
|
|
29
97
|
}
|
|
@@ -230,6 +230,59 @@ describe('BaseApiClient', () => {
|
|
|
230
230
|
})
|
|
231
231
|
})
|
|
232
232
|
|
|
233
|
+
it('maps the Digisac field errors (errors.<location>.<field>) into validationErrors', async () => {
|
|
234
|
+
mockFetch({
|
|
235
|
+
ok: false,
|
|
236
|
+
status: 400,
|
|
237
|
+
statusText: 'Bad Request',
|
|
238
|
+
headers: { 'Content-Type': 'application/json' },
|
|
239
|
+
body: JSON.stringify({
|
|
240
|
+
error: 'ValidationError',
|
|
241
|
+
message: 'The given data was invalid.',
|
|
242
|
+
status: 400,
|
|
243
|
+
errors: {
|
|
244
|
+
body: {
|
|
245
|
+
name: { messages: ['Required.'], types: ['required'] },
|
|
246
|
+
title: {
|
|
247
|
+
messages: ['Required.', 'Should contain lesser than or equals 255 characters.'],
|
|
248
|
+
types: ['required', 'hasLengthLesserThanOrEqual'],
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
params: { contactId: { messages: ['Should be an UUID v4 string.'], types: ['uuid4'] } },
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
})
|
|
255
|
+
await expect(client.post('/fail', {})).rejects.toMatchObject({
|
|
256
|
+
errorClass: 'ValidationError',
|
|
257
|
+
status: 400,
|
|
258
|
+
validationErrors: [
|
|
259
|
+
{ path: 'body.name', message: 'Required.', code: 'required' },
|
|
260
|
+
{ path: 'body.title', message: 'Required.', code: 'required' },
|
|
261
|
+
{
|
|
262
|
+
path: 'body.title',
|
|
263
|
+
message: 'Should contain lesser than or equals 255 characters.',
|
|
264
|
+
code: 'hasLengthLesserThanOrEqual',
|
|
265
|
+
},
|
|
266
|
+
{ path: 'params.contactId', message: 'Should be an UUID v4 string.', code: 'uuid4' },
|
|
267
|
+
],
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('keeps validationErrors null when the error body has no field errors', async () => {
|
|
272
|
+
mockFetch({
|
|
273
|
+
ok: false,
|
|
274
|
+
status: 400,
|
|
275
|
+
statusText: 'Bad Request',
|
|
276
|
+
headers: { 'Content-Type': 'application/json' },
|
|
277
|
+
body: JSON.stringify({
|
|
278
|
+
error: 'BadRequestHttpError',
|
|
279
|
+
message: 'Já existe uma funil com esse nome',
|
|
280
|
+
status: 400,
|
|
281
|
+
}),
|
|
282
|
+
})
|
|
283
|
+
await expect(client.get('/fail')).rejects.toMatchObject({ validationErrors: null })
|
|
284
|
+
})
|
|
285
|
+
|
|
233
286
|
it('falls back to default message when JSON parsing fails', async () => {
|
|
234
287
|
mockFetch({
|
|
235
288
|
ok: false,
|
|
@@ -6,6 +6,42 @@ export interface ValidationError {
|
|
|
6
6
|
code: string
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Digisac field errors: `{ errors: { <location>: { <field>: { messages: string[], types: string[] } } } }`,
|
|
11
|
+
* where location is `body`, `params`, `query`, `headers` or `cookies` (backend `validate` middleware).
|
|
12
|
+
*/
|
|
13
|
+
type DigisacFieldErrors = Record<string, Record<string, { messages?: unknown; types?: unknown }>>
|
|
14
|
+
|
|
15
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
16
|
+
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Flattens the Digisac `errors` object into `ValidationError[]` — one entry per message, with
|
|
20
|
+
* `path` = `<location>.<field>` (e.g. `body.name`) and `code` = the rule type (e.g. `required`).
|
|
21
|
+
* Returns `null` when the body carries no field errors.
|
|
22
|
+
*/
|
|
23
|
+
function parseValidationErrors(errors: unknown): ValidationError[] | null {
|
|
24
|
+
if (!isRecord(errors)) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const parsed: ValidationError[] = []
|
|
29
|
+
for (const [location, fields] of Object.entries(errors as DigisacFieldErrors)) {
|
|
30
|
+
if (!isRecord(fields)) {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
for (const [field, detail] of Object.entries(fields)) {
|
|
34
|
+
const messages = Array.isArray(detail?.messages) ? detail.messages.map(String) : []
|
|
35
|
+
const types = Array.isArray(detail?.types) ? detail.types.map(String) : []
|
|
36
|
+
messages.forEach((message, index) => {
|
|
37
|
+
parsed.push({ path: `${location}.${field}`, message, code: types[index] ?? types[0] ?? '' })
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return parsed.length ? parsed : null
|
|
43
|
+
}
|
|
44
|
+
|
|
9
45
|
export class ApiError extends Error {
|
|
10
46
|
public readonly errorClass?: string | undefined
|
|
11
47
|
public readonly status?: number | undefined
|
|
@@ -76,7 +112,7 @@ export class BaseApiClient implements ApiClient {
|
|
|
76
112
|
const errorData = JSON.parse(errorText)
|
|
77
113
|
errorMessage = errorData.message || errorData.error || errorMessage
|
|
78
114
|
errorClass = errorData.error
|
|
79
|
-
validationErrors = errorData.validationErrors ??
|
|
115
|
+
validationErrors = errorData.validationErrors ?? parseValidationErrors(errorData.errors)
|
|
80
116
|
}
|
|
81
117
|
} catch {
|
|
82
118
|
// If JSON parsing fails, use the default error message
|
|
@@ -5,7 +5,7 @@ import type { Department, DepartmentRelationships } from '../apis/departments'
|
|
|
5
5
|
import type { Message, MessageRelationships } from '../apis/messages'
|
|
6
6
|
import type { Organization, OrganizationRelationships } from '../apis/organizations'
|
|
7
7
|
import type { Person, PersonRelationships } from '../apis/people'
|
|
8
|
-
import type {
|
|
8
|
+
import type { PipelineRecord } from '../apis/pipeline'
|
|
9
9
|
import type { QuickReply, QuickReplyRelationships } from '../apis/quickReplies'
|
|
10
10
|
import type { Role, RoleRelationships } from '../apis/roles'
|
|
11
11
|
import type { Service, ServiceRelationships } from '../apis/services'
|
|
@@ -119,8 +119,8 @@ export interface WebhookEventPayloadMap {
|
|
|
119
119
|
'campaign.created': Omit<Campaign, CampaignRelationships>
|
|
120
120
|
'campaign.updated': Omit<Campaign, CampaignRelationships>
|
|
121
121
|
'campaign.destroyed': Omit<Campaign, CampaignRelationships>
|
|
122
|
-
'pipeline.created':
|
|
123
|
-
'pipeline.updated':
|
|
122
|
+
'pipeline.created': PipelineRecord
|
|
123
|
+
'pipeline.updated': PipelineRecord
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
// ─── Webhook Envelope ────────────────────────────────────────────────────────
|