n8n-nodes-salvia 0.2.0 → 0.4.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.
@@ -6,8 +6,16 @@ const STAGE_TYPE_LABEL = {
6
6
  won: " (ganho)",
7
7
  lost: " (perda)",
8
8
  };
9
+ const FIELD_TYPE_LABEL = {
10
+ text: "texto",
11
+ number: "número",
12
+ date: "data",
13
+ datetime: "data e hora",
14
+ boolean: "sim/não",
15
+ select: "seleção",
16
+ };
9
17
  async function fetchCatalog(ctx) {
10
- var _a, _b;
18
+ var _a, _b, _c, _d;
11
19
  const creds = (await ctx.getCredentials("salviaApi"));
12
20
  const res = (await ctx.helpers.httpRequestWithAuthentication.call(ctx, "salviaApi", {
13
21
  method: "GET",
@@ -15,21 +23,24 @@ async function fetchCatalog(ctx) {
15
23
  url: "/api/v1/catalog",
16
24
  json: true,
17
25
  }));
18
- return (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.pipelines) !== null && _b !== void 0 ? _b : [];
26
+ return {
27
+ pipelines: (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.pipelines) !== null && _b !== void 0 ? _b : [],
28
+ customFields: (_d = (_c = res.data) === null || _c === void 0 ? void 0 : _c.customFields) !== null && _d !== void 0 ? _d : [],
29
+ };
19
30
  }
20
31
  class Salvia {
21
32
  constructor() {
22
33
  this.methods = {
23
34
  loadOptions: {
24
35
  async getPipelines() {
25
- const pipelines = await fetchCatalog(this);
36
+ const { pipelines } = await fetchCatalog(this);
26
37
  return [
27
38
  { name: "— Funil padrão do Salvia —", value: "" },
28
39
  ...pipelines.map((p) => ({ name: p.name, value: p.id })),
29
40
  ];
30
41
  },
31
42
  async getStages() {
32
- const pipelines = await fetchCatalog(this);
43
+ const { pipelines } = await fetchCatalog(this);
33
44
  const pipelineId = this.getCurrentNodeParameter("pipelineId") || "";
34
45
  // Sem funil escolhido, lista as etapas de todos com o funil no rótulo —
35
46
  // escolher a etapa já basta (a API deriva o funil dela).
@@ -46,6 +57,20 @@ class Salvia {
46
57
  })),
47
58
  ];
48
59
  },
60
+ async getCustomFields() {
61
+ const { customFields } = await fetchCatalog(this);
62
+ return customFields.map((f) => {
63
+ var _a;
64
+ const opt = {
65
+ name: `${f.label} (${(_a = FIELD_TYPE_LABEL[f.type]) !== null && _a !== void 0 ? _a : f.type})`,
66
+ value: f.key,
67
+ };
68
+ if (f.type === "select" && f.options.length) {
69
+ opt.description = `Opções: ${f.options.join(", ")}`;
70
+ }
71
+ return opt;
72
+ });
73
+ },
49
74
  },
50
75
  };
51
76
  this.description = {
@@ -191,6 +216,71 @@ class Salvia {
191
216
  },
192
217
  },
193
218
  },
219
+ {
220
+ displayName: "Rastrear campanha (UTMs e click ids)",
221
+ name: "autoUtm",
222
+ type: "boolean",
223
+ default: true,
224
+ description: "Envia utm_source, utm_medium, utm_campaign, utm_term, utm_content, gclid e fbclid do próprio payload que chegou (nomes padrão). Desligue se preferir preencher à mão nas Opções adicionais.",
225
+ displayOptions: { show: { resource: ["lead"], operation: ["create"] } },
226
+ routing: {
227
+ request: {
228
+ body: {
229
+ // Aceita o payload cru e o de webhook (que aninha em `body`).
230
+ // undefined em vez de "" porque a API descarta chave ausente, e
231
+ // string vazia sobrescreveria UTM que o lead já tinha.
232
+ utmSource: "={{ $value ? ($json.body?.utm_source || $json.utm_source || undefined) : undefined }}",
233
+ utmMedium: "={{ $value ? ($json.body?.utm_medium || $json.utm_medium || undefined) : undefined }}",
234
+ utmCampaign: "={{ $value ? ($json.body?.utm_campaign || $json.utm_campaign || undefined) : undefined }}",
235
+ utmTerm: "={{ $value ? ($json.body?.utm_term || $json.utm_term || undefined) : undefined }}",
236
+ utmContent: "={{ $value ? ($json.body?.utm_content || $json.utm_content || undefined) : undefined }}",
237
+ // Click ids vêm no mesmo pacote e pela mesma razão: sem eles a
238
+ // campanha não fica sabendo quais cliques viraram cliente.
239
+ gclid: "={{ $value ? ($json.body?.gclid || $json.gclid || undefined) : undefined }}",
240
+ fbclid: "={{ $value ? ($json.body?.fbclid || $json.fbclid || undefined) : undefined }}",
241
+ },
242
+ },
243
+ },
244
+ },
245
+ {
246
+ displayName: "Campos personalizados",
247
+ name: "customFieldsUi",
248
+ type: "fixedCollection",
249
+ typeOptions: { multipleValues: true },
250
+ placeholder: "Adicionar campo personalizado",
251
+ default: {},
252
+ description: "Campos criados na sua conta do Salvia. O dropdown carrega direto de lá — crie o campo no Salvia e ele aparece aqui.",
253
+ displayOptions: { show: { resource: ["lead"], operation: ["create"] } },
254
+ options: [
255
+ {
256
+ displayName: "Campo",
257
+ name: "values",
258
+ values: [
259
+ {
260
+ displayName: "Campo",
261
+ name: "key",
262
+ type: "options",
263
+ typeOptions: { loadOptionsMethod: "getCustomFields" },
264
+ default: "",
265
+ },
266
+ {
267
+ displayName: "Valor",
268
+ name: "value",
269
+ type: "string",
270
+ default: "",
271
+ },
272
+ ],
273
+ },
274
+ ],
275
+ routing: {
276
+ send: {
277
+ type: "body",
278
+ property: "customFields",
279
+ // Vira {"chave": "valor"} no corpo; sem linhas preenchidas, nem vai.
280
+ value: "={{ (($value && $value.values) || []).filter(v => v.key).length ? Object.fromEntries($value.values.filter(v => v.key).map(v => [v.key, v.value])) : undefined }}",
281
+ },
282
+ },
283
+ },
194
284
  {
195
285
  displayName: "Opções adicionais",
196
286
  name: "additionalFields",
@@ -208,40 +298,45 @@ class Salvia {
208
298
  routing: { send: { type: "body", property: "ownerEmail" } },
209
299
  },
210
300
  {
211
- displayName: "Campos personalizados (JSON)",
212
- name: "customFields",
213
- type: "json",
214
- default: "{}",
215
- description: 'Ex.: {"convenio": "Unimed", "procedimento": "Botox"}',
216
- routing: {
217
- send: {
218
- type: "body",
219
- property: "customFields",
220
- value: "={{ typeof $value === 'string' ? JSON.parse($value || '{}') : $value }}",
221
- },
222
- },
223
- },
224
- {
225
- displayName: "UTM Source",
301
+ displayName: "UTM Source (manual)",
226
302
  name: "utmSource",
227
303
  type: "string",
228
304
  default: "",
305
+ description: "Sobrescreve o valor pego do payload",
229
306
  routing: { send: { type: "body", property: "utmSource" } },
230
307
  },
231
308
  {
232
- displayName: "UTM Medium",
309
+ displayName: "UTM Medium (manual)",
233
310
  name: "utmMedium",
234
311
  type: "string",
235
312
  default: "",
313
+ description: "Sobrescreve o valor pego do payload",
236
314
  routing: { send: { type: "body", property: "utmMedium" } },
237
315
  },
238
316
  {
239
- displayName: "UTM Campaign",
317
+ displayName: "UTM Campaign (manual)",
240
318
  name: "utmCampaign",
241
319
  type: "string",
242
320
  default: "",
321
+ description: "Sobrescreve o valor pego do payload",
243
322
  routing: { send: { type: "body", property: "utmCampaign" } },
244
323
  },
324
+ {
325
+ displayName: "UTM Term (manual)",
326
+ name: "utmTerm",
327
+ type: "string",
328
+ default: "",
329
+ description: "Sobrescreve o valor pego do payload",
330
+ routing: { send: { type: "body", property: "utmTerm" } },
331
+ },
332
+ {
333
+ displayName: "UTM Content (manual)",
334
+ name: "utmContent",
335
+ type: "string",
336
+ default: "",
337
+ description: "Sobrescreve o valor pego do payload",
338
+ routing: { send: { type: "body", property: "utmContent" } },
339
+ },
245
340
  {
246
341
  displayName: "Permitir telefone duplicado",
247
342
  name: "forceDuplicate",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-salvia",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Nodes do Salvia CRM para n8n — crie leads e integre sua clínica aos seus fluxos.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",