@fayz-ai/plugin-marketing 0.12.0 → 0.12.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.
@@ -1 +1 @@
1
- {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/data/mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAA2C,MAAM,SAAS,CAAA;AAC7F,OAAO,KAAK,EACV,kBAAkB,EAIlB,eAAe,EAQhB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AA6B1E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,UAAU,EAAE,eAAe,CAAA;IAC3B,MAAM,EAAE,eAAe,CAAA;CACxB;AAyGD,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,mBAAmB,GAAG,qBAAqB,CA0J3F"}
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/data/mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAA2C,MAAM,SAAS,CAAA;AAC7F,OAAO,KAAK,EACV,kBAAkB,EAIlB,eAAe,EAQhB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AA6B1E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,UAAU,EAAE,eAAe,CAAA;IAC3B,MAAM,EAAE,eAAe,CAAA;CACxB;AAkHD,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,mBAAmB,GAAG,qBAAqB,CAmK3F"}
@@ -1 +1 @@
1
- {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EAGrB,sBAAsB,EACvB,MAAM,SAAS,CAAA;AAChB,OAAO,KAAK,EACV,kBAAkB,EAGlB,eAAe,EAQhB,MAAM,UAAU,CAAA;AAYjB,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,UAAU,EAAE,eAAe,CAAA;IAC3B,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,WAAW,CAAC,EAAE,sBAAsB,CAAA;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxE;AAgFD,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,qBAAqB,CA2evB"}
1
+ {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EAGrB,sBAAsB,EACvB,MAAM,SAAS,CAAA;AAChB,OAAO,KAAK,EACV,kBAAkB,EAGlB,eAAe,EAQhB,MAAM,UAAU,CAAA;AAYjB,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,UAAU,EAAE,eAAe,CAAA;IAC3B,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,WAAW,CAAC,EAAE,sBAAsB,CAAA;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxE;AAqFD,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,qBAAqB,CAkfvB"}
@@ -1,4 +1,4 @@
1
- import type { Campaign, CampaignStatus, ChannelPerformance, DateRangeKey, FunnelStage, LandingPage, LandingPagePerf, LandingPageStatus, MarketingOverview, MarketingQuery, MarketingSite } from '../types';
1
+ import type { Campaign, CampaignStatus, ChannelPerformance, DateRangeKey, FunnelStage, LandingPage, LandingPagePerf, LandingPageStatus, MarketingOverview, MarketingQuery, MarketingSite, CaptureDestination, CaptureField } from '../types';
2
2
  export interface SaveCampaignInput {
3
3
  id?: string;
4
4
  name: string;
@@ -17,6 +17,11 @@ export interface SaveLandingPageInput {
17
17
  status?: LandingPageStatus;
18
18
  prompt?: string;
19
19
  isActive?: boolean;
20
+ captureDestination?: CaptureDestination;
21
+ captureRequiredFields?: CaptureField[];
22
+ /** `null` limpa; `undefined` não toca. */
23
+ minAdvanceHours?: number | null;
24
+ maxAdvanceDays?: number | null;
20
25
  }
21
26
  export interface MarketingDataProvider {
22
27
  overview(query: MarketingQuery): Promise<MarketingOverview>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/data/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/data/types.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -431,6 +431,9 @@ function FunnelView() {
431
431
  ] })
432
432
  ] });
433
433
  }
434
+
435
+ // src/types.ts
436
+ var CAPTURE_FIELDS = ["name", "phone", "email", "document", "notes"];
434
437
  function slugify(value) {
435
438
  return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9:]+/g, "-").replace(/^-+|-+$/g, "");
436
439
  }
@@ -450,6 +453,10 @@ function LandingPageComposer({
450
453
  const [prompt, setPrompt] = React9.useState("");
451
454
  const [tags, setTags] = React9.useState([]);
452
455
  const [newTag, setNewTag] = React9.useState("");
456
+ const [destination, setDestination] = React9.useState("lead");
457
+ const [required, setRequired] = React9.useState(["name"]);
458
+ const [minH, setMinH] = React9.useState("");
459
+ const [maxD, setMaxD] = React9.useState("");
453
460
  const [saving, setSaving] = React9.useState(false);
454
461
  React9.useEffect(() => {
455
462
  if (!open) return;
@@ -460,6 +467,10 @@ function LandingPageComposer({
460
467
  setPrompt(page?.prompt ?? "");
461
468
  setTags(page?.leadTags ?? []);
462
469
  setNewTag("");
470
+ setDestination(page?.captureDestination ?? "lead");
471
+ setRequired(page?.captureRequiredFields?.length ? page.captureRequiredFields : ["name"]);
472
+ setMinH(page?.minAdvanceHours != null ? String(page.minAdvanceHours) : "");
473
+ setMaxD(page?.maxAdvanceDays != null ? String(page.maxAdvanceDays) : "");
463
474
  }, [open, page, sites]);
464
475
  const site = sites.find((s) => s.id === siteId);
465
476
  const previewPath = route.trim() ? route.trim().startsWith("/") ? route.trim() : `/${route.trim()}` : "";
@@ -488,7 +499,11 @@ function LandingPageComposer({
488
499
  route: route.trim(),
489
500
  leadTags: tags,
490
501
  status,
491
- prompt: status === "planned" ? prompt.trim() || void 0 : void 0
502
+ prompt: status === "planned" ? prompt.trim() || void 0 : void 0,
503
+ captureDestination: destination,
504
+ captureRequiredFields: required,
505
+ minAdvanceHours: destination !== "lead" && minH ? Number(minH) : null,
506
+ maxAdvanceDays: destination !== "lead" && maxD ? Number(maxD) : null
492
507
  });
493
508
  onClose();
494
509
  } finally {
@@ -602,6 +617,53 @@ function LandingPageComposer({
602
617
  )
603
618
  ] }),
604
619
  /* @__PURE__ */ jsx("span", { className: "block text-xs text-muted-foreground", children: t("marketing.landing.leadTagHint") })
620
+ ] }),
621
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-card border bg-muted/20 p-3", "data-testid": "landing-capture", children: [
622
+ /* @__PURE__ */ jsxs("div", { children: [
623
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("marketing.landing.formTitle") }),
624
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("marketing.landing.formHint") })
625
+ ] }),
626
+ /* @__PURE__ */ jsxs("div", { children: [
627
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium", children: t("marketing.landing.destination") }),
628
+ /* @__PURE__ */ jsxs(Select, { value: destination, onValueChange: (v) => setDestination(v), children: [
629
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-9 text-sm", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
630
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
631
+ /* @__PURE__ */ jsx(SelectItem, { value: "lead", children: t("marketing.landing.dest.lead") }),
632
+ /* @__PURE__ */ jsx(SelectItem, { value: "booking", children: t("marketing.landing.dest.booking") }),
633
+ /* @__PURE__ */ jsx(SelectItem, { value: "reservation", children: t("marketing.landing.dest.reservation") })
634
+ ] })
635
+ ] })
636
+ ] }),
637
+ /* @__PURE__ */ jsxs("div", { children: [
638
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium", children: t("marketing.landing.required") }),
639
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: t("marketing.landing.requiredHint") }),
640
+ /* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap gap-1.5", children: CAPTURE_FIELDS.map((f) => {
641
+ const on = required.includes(f);
642
+ return /* @__PURE__ */ jsx(
643
+ "button",
644
+ {
645
+ type: "button",
646
+ className: `rounded-full border px-2.5 py-1 text-xs ${on ? "border-primary bg-primary/10 text-primary" : "text-muted-foreground"}`,
647
+ onClick: () => setRequired((prev) => on ? prev.filter((x) => x !== f) : [...prev, f]),
648
+ children: t(`marketing.landing.field.${f}`)
649
+ },
650
+ f
651
+ );
652
+ }) })
653
+ ] }),
654
+ destination !== "lead" && /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", "data-testid": "landing-window", children: [
655
+ /* @__PURE__ */ jsxs("div", { children: [
656
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium", children: t("marketing.landing.minAdvance") }),
657
+ /* @__PURE__ */ jsx(Input, { type: "number", min: 0, value: minH, onChange: (e) => setMinH(e.target.value), placeholder: "2" }),
658
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: t("marketing.landing.minAdvanceHint") })
659
+ ] }),
660
+ /* @__PURE__ */ jsxs("div", { children: [
661
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium", children: t("marketing.landing.maxAdvance") }),
662
+ /* @__PURE__ */ jsx(Input, { type: "number", min: 1, value: maxD, onChange: (e) => setMaxD(e.target.value), placeholder: "60" }),
663
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: t("marketing.landing.maxAdvanceHint") })
664
+ ] })
665
+ ] }),
666
+ page?.captureKey && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: t("marketing.landing.publicKey", { key: page.captureKey }) })
605
667
  ] })
606
668
  ] }),
607
669
  /* @__PURE__ */ jsx(SheetFooter, { children: /* @__PURE__ */ jsx(
@@ -3372,8 +3434,40 @@ var MOCK_LEAD_TAGS = [
3372
3434
  ];
3373
3435
  function seedLandingPages() {
3374
3436
  return [
3375
- { id: "lp-1", siteId: MOCK_SITE.id, name: "Consulta gratuita", route: "/consulta", leadTags: ["lp-consulta"], status: "live", isActive: true, visits: 3820, conversions: 474 },
3376
- { id: "lp-2", siteId: MOCK_SITE.id, name: "Promo da esta\xE7\xE3o", route: "/promo", leadTags: ["lp-promo"], status: "live", isActive: true, visits: 1560, conversions: 126 }
3437
+ // Duas portas com destinos DIFERENTES, porque é o que a tela precisa
3438
+ // provar: a de agendamento mostra a janela de antecedência, a de contato
3439
+ // não — oferecer "mínimo 2h" num formulário de contato prometeria uma regra
3440
+ // sem sobre o que valer.
3441
+ {
3442
+ id: "lp-1",
3443
+ siteId: MOCK_SITE.id,
3444
+ name: "Consulta gratuita",
3445
+ route: "/consulta",
3446
+ leadTags: ["lp-consulta"],
3447
+ status: "live",
3448
+ isActive: true,
3449
+ captureKey: "Kx7mQp2rTn9vBw4sYd6h",
3450
+ captureDestination: "booking",
3451
+ captureRequiredFields: ["name", "phone"],
3452
+ minAdvanceHours: 2,
3453
+ maxAdvanceDays: 60,
3454
+ visits: 3820,
3455
+ conversions: 474
3456
+ },
3457
+ {
3458
+ id: "lp-2",
3459
+ siteId: MOCK_SITE.id,
3460
+ name: "Promo da esta\xE7\xE3o",
3461
+ route: "/promo",
3462
+ leadTags: ["lp-promo"],
3463
+ status: "live",
3464
+ isActive: true,
3465
+ captureKey: "Lp3nRt8wZc5vMq1xJh7k",
3466
+ captureDestination: "lead",
3467
+ captureRequiredFields: ["name", "email"],
3468
+ visits: 1560,
3469
+ conversions: 126
3470
+ }
3377
3471
  ];
3378
3472
  }
3379
3473
  function normalizeRoute(route) {
@@ -3498,7 +3592,11 @@ function createMockMarketingProvider(cfg) {
3498
3592
  leadTags: input.leadTags ?? existing.leadTags,
3499
3593
  status: input.status ?? existing.status,
3500
3594
  prompt: input.prompt,
3501
- isActive: input.isActive ?? existing.isActive
3595
+ isActive: input.isActive ?? existing.isActive,
3596
+ captureDestination: input.captureDestination ?? existing.captureDestination,
3597
+ captureRequiredFields: input.captureRequiredFields ?? existing.captureRequiredFields,
3598
+ minAdvanceHours: input.captureDestination === "lead" ? void 0 : input.minAdvanceHours ?? existing.minAdvanceHours ?? void 0,
3599
+ maxAdvanceDays: input.captureDestination === "lead" ? void 0 : input.maxAdvanceDays ?? existing.maxAdvanceDays ?? void 0
3502
3600
  });
3503
3601
  return existing;
3504
3602
  }
@@ -3511,6 +3609,11 @@ function createMockMarketingProvider(cfg) {
3511
3609
  status: input.status ?? "live",
3512
3610
  prompt: input.prompt,
3513
3611
  isActive: input.isActive ?? true,
3612
+ captureKey: `mock-${Math.abs(input.name.length * 7919).toString(36)}`,
3613
+ captureDestination: input.captureDestination ?? "lead",
3614
+ captureRequiredFields: input.captureRequiredFields ?? [],
3615
+ minAdvanceHours: input.captureDestination === "lead" ? void 0 : input.minAdvanceHours ?? void 0,
3616
+ maxAdvanceDays: input.captureDestination === "lead" ? void 0 : input.maxAdvanceDays ?? void 0,
3514
3617
  visits: 0,
3515
3618
  conversions: 0
3516
3619
  };
@@ -3565,7 +3668,12 @@ function landingPageFromRow(row) {
3565
3668
  leadTags: row.lead_tags ?? [],
3566
3669
  status: row.status === "planned" ? "planned" : "live",
3567
3670
  prompt: row.prompt ?? void 0,
3568
- isActive: row.is_active !== false
3671
+ isActive: row.is_active !== false,
3672
+ captureKey: row.capture_key ?? void 0,
3673
+ captureDestination: row.capture_destination ?? "lead",
3674
+ captureRequiredFields: row.capture_required_fields ?? [],
3675
+ minAdvanceHours: row.min_advance_hours ?? void 0,
3676
+ maxAdvanceDays: row.max_advance_days ?? void 0
3569
3677
  };
3570
3678
  }
3571
3679
  function createSupabaseMarketingProvider(options) {
@@ -3895,6 +4003,13 @@ function createSupabaseMarketingProvider(options) {
3895
4003
  status: input.status ?? "live",
3896
4004
  prompt: input.prompt?.trim() || null,
3897
4005
  is_active: input.isActive ?? true,
4006
+ capture_destination: input.captureDestination ?? "lead",
4007
+ capture_required_fields: input.captureRequiredFields ?? [],
4008
+ // A janela só faz sentido onde há horário para marcar. Num destino
4009
+ // 'lead' ela é nula de propósito: exigir "no mínimo 2h de antecedência"
4010
+ // de um formulário de contato não quer dizer nada.
4011
+ min_advance_hours: input.captureDestination === "lead" ? null : input.minAdvanceHours ?? null,
4012
+ max_advance_days: input.captureDestination === "lead" ? null : input.maxAdvanceDays ?? null,
3898
4013
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
3899
4014
  };
3900
4015
  const query = input.id ? getClient().from(T.landingPages).update(payload).eq("id", input.id) : getClient().from(T.landingPages).insert(payload);
@@ -5699,9 +5814,197 @@ GRANT ALL ON TABLE public.v_marketing_landing_pages TO service_role;
5699
5814
 
5700
5815
  DROP VIEW IF EXISTS public.fayz_projects;
5701
5816
  `;
5817
+ var MIGRATION_003_A_PORTA_PUBLICA_E_A_PAGINA = `-- 003_a_porta_publica_e_a_pagina.sql
5818
+ --
5819
+ -- Havia DUAS portas p\xFAblicas para a mesma coisa.
5820
+ --
5821
+ -- A landing page \xE9 uma p\xE1gina com endere\xE7o p\xFAblico que existe para capturar
5822
+ -- gente. A "p\xE1gina de captura" (crm/007) \xE9 um endere\xE7o p\xFAblico que existe para
5823
+ -- capturar gente. A diferen\xE7a que eu tinha em mente \u2014 uma \xE9 conte\xFAdo, a outra \xE9
5824
+ -- regra \u2014 n\xE3o \xE9 uma diferen\xE7a que o lojista faz: ele quer UMA porta, com o
5825
+ -- texto e as regras dela no mesmo lugar.
5826
+ --
5827
+ -- Ent\xE3o a regra entra na p\xE1gina. A landing page ganha o que s\xF3 a captura tinha:
5828
+ -- a chave opaca, o destino, os campos obrigat\xF3rios e a janela de anteced\xEAncia.
5829
+ -- E as duas fun\xE7\xF5es que \`anon\` executa passam a ler daqui.
5830
+ --
5831
+ -- POR QUE AS FUN\xC7\xD5ES MUDAM DE PLUGIN: a corrente aplica \`crm\` ANTES de
5832
+ -- \`marketing\`, ent\xE3o uma fun\xE7\xE3o do CRM n\xE3o pode ler uma tabela de marketing \u2014
5833
+ -- num replay do zero ela apontaria para algo que ainda n\xE3o existe. Captura
5834
+ -- passou a ser assunto de marketing, e a fun\xE7\xE3o vai junto.
5835
+
5836
+ ALTER TABLE public.plg_marketing_landing_pages
5837
+ -- O identificador P\xDABLICO e opaco. Continua sendo o que vai na URL; um slug
5838
+ -- adivinh\xE1vel deixaria achar a p\xE1gina de outra casa escrevendo o nome dela.
5839
+ ADD COLUMN IF NOT EXISTS capture_key text,
5840
+ -- Em que sala a porta d\xE1. "Agendamento" \xE9 palavra de sal\xE3o; num restaurante a
5841
+ -- mesma p\xE1gina marca MESA, e numa loja s\xF3 captura contato.
5842
+ ADD COLUMN IF NOT EXISTS capture_destination text DEFAULT 'lead' NOT NULL,
5843
+ ADD COLUMN IF NOT EXISTS capture_required_fields text[] DEFAULT '{}'::text[] NOT NULL,
5844
+ -- A janela \xE9 dupla: piso em horas contra "daqui a dez minutos", teto em dias
5845
+ -- contra "o ano que vem". Nulas quando o destino n\xE3o marca nada.
5846
+ ADD COLUMN IF NOT EXISTS min_advance_hours integer,
5847
+ ADD COLUMN IF NOT EXISTS max_advance_days integer;
5848
+
5849
+ DO $$ BEGIN
5850
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_marketing_landing_pages_capture_key_key') THEN
5851
+ ALTER TABLE ONLY public.plg_marketing_landing_pages
5852
+ ADD CONSTRAINT plg_marketing_landing_pages_capture_key_key UNIQUE (capture_key);
5853
+ END IF;
5854
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_marketing_landing_pages_destination') THEN
5855
+ ALTER TABLE public.plg_marketing_landing_pages
5856
+ ADD CONSTRAINT plg_marketing_landing_pages_destination
5857
+ CHECK (capture_destination = ANY (ARRAY['lead'::text, 'booking'::text, 'reservation'::text]));
5858
+ END IF;
5859
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_marketing_landing_pages_window') THEN
5860
+ ALTER TABLE public.plg_marketing_landing_pages
5861
+ ADD CONSTRAINT plg_marketing_landing_pages_window CHECK (
5862
+ min_advance_hours IS NULL OR max_advance_days IS NULL
5863
+ OR (min_advance_hours >= 0 AND max_advance_days >= 1
5864
+ AND min_advance_hours <= max_advance_days * 24));
5865
+ END IF;
5866
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_marketing_landing_pages_fields') THEN
5867
+ ALTER TABLE public.plg_marketing_landing_pages
5868
+ ADD CONSTRAINT plg_marketing_landing_pages_fields CHECK (
5869
+ capture_required_fields <@ ARRAY['name','phone','email','document','notes']::text[]);
5870
+ END IF;
5871
+ END $$;
5872
+
5873
+ COMMENT ON COLUMN public.plg_marketing_landing_pages.capture_key IS
5874
+ 'A chave P\xDABLICA e opaca da p\xE1gina. Uma porta s\xF3: conte\xFAdo e regra no mesmo lugar (marketing/003).';
5875
+
5876
+ -- A chave nasce sozinha, com o mesmo auxiliar do token de convite (crm/003),
5877
+ -- que resolve o pgcrypto em tempo de execu\xE7\xE3o.
5878
+ CREATE OR REPLACE FUNCTION public.trg_marketing_capture_key()
5879
+ RETURNS trigger LANGUAGE plpgsql SET search_path TO '' AS $$
5880
+ BEGIN
5881
+ IF NEW.capture_key IS NULL OR btrim(NEW.capture_key) = '' THEN
5882
+ NEW.capture_key := public.crm_url_token(18);
5883
+ END IF;
5884
+ RETURN NEW;
5885
+ END $$;
5886
+
5887
+ DROP TRIGGER IF EXISTS plg_marketing_capture_key ON public.plg_marketing_landing_pages;
5888
+ CREATE TRIGGER plg_marketing_capture_key
5889
+ BEFORE INSERT ON public.plg_marketing_landing_pages
5890
+ FOR EACH ROW EXECUTE FUNCTION public.trg_marketing_capture_key();
5891
+
5892
+ UPDATE public.plg_marketing_landing_pages
5893
+ SET capture_key = public.crm_url_token(18)
5894
+ WHERE capture_key IS NULL;
5895
+
5896
+ -- \u2500\u2500 abrir a porta (quem est\xE1 de fora) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5897
+ --
5898
+ -- \`anon\` executa: devolve o m\xEDnimo para desenhar a p\xE1gina e NADA que sirva para
5899
+ -- descobrir outra coisa. N\xE3o lista p\xE1ginas, n\xE3o aceita id (s\xF3 a chave), n\xE3o
5900
+ -- devolve tenant_id. P\xE1gina inativa responde \`closed\` e continua existindo \u2014 um
5901
+ -- 404 num endere\xE7o que o cliente tem salvo parece site fora do ar.
5902
+ CREATE OR REPLACE FUNCTION public.marketing_capture_load(p_key text)
5903
+ RETURNS jsonb
5904
+ LANGUAGE plpgsql SECURITY DEFINER STABLE SET search_path TO ''
5905
+ AS $$
5906
+ DECLARE v record;
5907
+ BEGIN
5908
+ IF p_key IS NULL OR length(p_key) < 8 THEN
5909
+ RETURN jsonb_build_object('status', 'not_found');
5910
+ END IF;
5911
+ SELECT * INTO v FROM public.plg_marketing_landing_pages WHERE capture_key = p_key;
5912
+ IF v.id IS NULL THEN RETURN jsonb_build_object('status', 'not_found'); END IF;
5913
+
5914
+ RETURN jsonb_build_object(
5915
+ 'status', CASE WHEN v.is_active AND v.status = 'live' THEN 'ok' ELSE 'closed' END,
5916
+ 'name', v.name,
5917
+ 'route', v.route,
5918
+ 'destination', v.capture_destination,
5919
+ 'requiredFields', to_jsonb(v.capture_required_fields),
5920
+ 'leadTags', to_jsonb(v.lead_tags),
5921
+ -- A janela vai junto para a p\xE1gina APAGAR os hor\xE1rios imposs\xEDveis, em vez
5922
+ -- de deixar o cliente escolher um e levar um "n\xE3o" depois.
5923
+ 'minAdvanceHours', v.min_advance_hours,
5924
+ 'maxAdvanceDays', v.max_advance_days
5925
+ );
5926
+ END $$;
5927
+
5928
+ REVOKE ALL ON FUNCTION public.marketing_capture_load(text) FROM PUBLIC;
5929
+ GRANT EXECUTE ON FUNCTION public.marketing_capture_load(text) TO anon, authenticated, service_role;
5930
+
5931
+ -- \u2500\u2500 a janela, aplicada no servidor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5932
+ --
5933
+ -- Uma janela que s\xF3 existe na tela \xE9 decora\xE7\xE3o: quem manda um POST direto passa
5934
+ -- por cima. Devolve o MOTIVO porque "cedo demais" e "longe demais" pedem
5935
+ -- corre\xE7\xF5es opostas, e uma recusa gen\xE9rica faz o cliente tentar de novo errado.
5936
+ CREATE OR REPLACE FUNCTION public.marketing_capture_window_check(p_key text, p_starts_at timestamp with time zone)
5937
+ RETURNS jsonb
5938
+ LANGUAGE plpgsql SECURITY DEFINER STABLE SET search_path TO ''
5939
+ AS $$
5940
+ DECLARE v record;
5941
+ BEGIN
5942
+ SELECT * INTO v FROM public.plg_marketing_landing_pages WHERE capture_key = p_key;
5943
+ IF v.id IS NULL THEN RETURN jsonb_build_object('ok', false, 'reason', 'not_found'); END IF;
5944
+ IF NOT (v.is_active AND v.status = 'live') THEN RETURN jsonb_build_object('ok', false, 'reason', 'closed'); END IF;
5945
+ IF p_starts_at IS NULL THEN RETURN jsonb_build_object('ok', true); END IF;
5946
+
5947
+ IF v.min_advance_hours IS NOT NULL
5948
+ AND p_starts_at < now() + make_interval(hours => v.min_advance_hours) THEN
5949
+ RETURN jsonb_build_object('ok', false, 'reason', 'too_soon', 'minAdvanceHours', v.min_advance_hours);
5950
+ END IF;
5951
+ IF v.max_advance_days IS NOT NULL
5952
+ AND p_starts_at > now() + make_interval(days => v.max_advance_days) THEN
5953
+ RETURN jsonb_build_object('ok', false, 'reason', 'too_far', 'maxAdvanceDays', v.max_advance_days);
5954
+ END IF;
5955
+ RETURN jsonb_build_object('ok', true);
5956
+ END $$;
5957
+
5958
+ REVOKE ALL ON FUNCTION public.marketing_capture_window_check(text, timestamp with time zone) FROM PUBLIC;
5959
+ GRANT EXECUTE ON FUNCTION public.marketing_capture_window_check(text, timestamp with time zone) TO anon, authenticated, service_role;
5960
+
5961
+ -- \u2500\u2500 a porta antiga vem para a p\xE1gina \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5962
+ --
5963
+ -- Cada \`plg_crm_capture_pages\` vira uma landing page \`planned\` com a mesma
5964
+ -- chave \u2014 a chave \xE9 o endere\xE7o que o cliente pode ter salvo, e troc\xE1-la
5965
+ -- quebraria todo link j\xE1 colado num site. Nasce como \`planned\` e n\xE3o \`live\`
5966
+ -- porque a p\xE1gina ainda n\xE3o tem conte\xFAdo: publicar automaticamente poria no ar
5967
+ -- um endere\xE7o em branco.
5968
+ DO $$
5969
+ DECLARE r record; v_site uuid; v_n integer := 0;
5970
+ BEGIN
5971
+ IF to_regclass('public.plg_crm_capture_pages') IS NULL THEN RETURN; END IF;
5972
+
5973
+ FOR r IN SELECT * FROM public.plg_crm_capture_pages LOOP
5974
+ SELECT s.id INTO v_site FROM public.published_sites s
5975
+ WHERE s.tenant_id = r.tenant_id ORDER BY s.created_at LIMIT 1;
5976
+ -- Sem site n\xE3o h\xE1 onde a p\xE1gina morar. A porta \xE9 preservada no aviso, n\xE3o
5977
+ -- apagada em sil\xEAncio.
5978
+ IF v_site IS NULL THEN
5979
+ RAISE WARNING '003: tenant % tem porta de captura "%" e nenhum site \u2014 a porta n\xE3o foi migrada', r.tenant_id, r.name;
5980
+ CONTINUE;
5981
+ END IF;
5982
+
5983
+ INSERT INTO public.plg_marketing_landing_pages
5984
+ (tenant_id, site_id, name, route, status, is_active, lead_tags, unit_id,
5985
+ capture_key, capture_destination, capture_required_fields, min_advance_hours, max_advance_days)
5986
+ VALUES
5987
+ (r.tenant_id, v_site, r.name,
5988
+ '/' || coalesce(public.crm_slug(r.name), 'captura'),
5989
+ 'planned', r.is_active, r.lead_tags, r.unit_id,
5990
+ r.key, r.destination, r.required_fields, r.min_advance_hours, r.max_advance_days)
5991
+ ON CONFLICT DO NOTHING;
5992
+ v_n := v_n + 1;
5993
+ END LOOP;
5994
+
5995
+ RAISE NOTICE '003: % porta(s) de captura viraram landing page', v_n;
5996
+ END $$;
5997
+
5998
+ DROP TABLE IF EXISTS public.plg_crm_capture_pages;
5999
+ DELETE FROM app.scaffold_registry WHERE table_name = 'plg_crm_capture_pages';
6000
+ DROP FUNCTION IF EXISTS public.crm_capture_page_load(text);
6001
+ DROP FUNCTION IF EXISTS public.crm_capture_window_check(text, timestamp with time zone);
6002
+ DROP FUNCTION IF EXISTS public.trg_crm_capture_page_key();
6003
+ `;
5702
6004
  var MIGRATIONS = [
5703
6005
  { id: "000_baseline", sql: MIGRATION_000_BASELINE },
5704
- { id: "001_the_landing_page_names_the_site", sql: MIGRATION_001_THE_LANDING_PAGE_NAMES_THE_SITE }
6006
+ { id: "001_the_landing_page_names_the_site", sql: MIGRATION_001_THE_LANDING_PAGE_NAMES_THE_SITE },
6007
+ { id: "003_a_porta_publica_e_a_pagina", sql: MIGRATION_003_A_PORTA_PUBLICA_E_A_PAGINA }
5705
6008
  ];
5706
6009
 
5707
6010
  // src/integrations/ranklayer/settings.ts
@@ -6120,7 +6423,48 @@ var en = {
6120
6423
  "marketing.content.status.recording": "recording",
6121
6424
  "marketing.content.status.editing": "editing",
6122
6425
  "marketing.content.status.scheduled": "scheduled",
6123
- "marketing.content.status.published": "published"
6426
+ "marketing.content.status.published": "published",
6427
+ "marketing.brand.title": "Brand",
6428
+ "marketing.brand.subtitle": "What the assistant reads before writing anything for this business.",
6429
+ "marketing.brand.identity": "Identity",
6430
+ "marketing.brand.name": "Brand name",
6431
+ "marketing.brand.tagline": "Tagline",
6432
+ "marketing.brand.mission": "Positioning",
6433
+ "marketing.brand.voice": "How the brand speaks",
6434
+ "marketing.brand.voiceHint": "This rides along with every question asked of the assistant while marketing is open.",
6435
+ "marketing.brand.tone": "Tone of voice",
6436
+ "marketing.brand.tonePlaceholder": "Direct and warm. Talks like a person, not a brochure.",
6437
+ "marketing.brand.keywords": "Words the house uses",
6438
+ "marketing.brand.ctas": "Standard calls to action",
6439
+ "marketing.brand.ctasPlaceholder": "Book your slot, Message us on WhatsApp",
6440
+ "marketing.brand.hashtags": "Hashtags",
6441
+ "marketing.brand.avoid": "What the brand does not say",
6442
+ "marketing.brand.avoidHint": "The one field whose absence is invisible: nobody proofreads a piece looking for the word the house asked not to use.",
6443
+ "marketing.brand.avoidWords": "Words to avoid",
6444
+ "marketing.brand.avoidPlaceholder": "cheap, unmissable, flash sale",
6445
+ "marketing.brand.agentOn": "The assistant is receiving these standards and will follow them when writing.",
6446
+ "marketing.brand.agentOff": "Nothing filled in yet \u2014 the assistant writes with no brand guidance.",
6447
+ "marketing.brand.agentSummary": "The house brand kit: tone of voice, keywords, standard CTAs and the forbidden words.",
6448
+ "marketing.brand.saved": "Brand saved.",
6449
+ "marketing.brand.saveFailed": "Could not save the brand.",
6450
+ "marketing.landing.formTitle": "The page's form",
6451
+ "marketing.landing.formHint": "The page is the content; this is the door's rule \u2014 what it asks for and where it leads.",
6452
+ "marketing.landing.destination": "Where it leads",
6453
+ "marketing.landing.dest.lead": "Captures a contact",
6454
+ "marketing.landing.dest.booking": "Books a slot",
6455
+ "marketing.landing.dest.reservation": "Books a table",
6456
+ "marketing.landing.required": "What the house requires",
6457
+ "marketing.landing.requiredHint": "Every extra required field costs conversion. Ask for the minimum you need to reply.",
6458
+ "marketing.landing.field.name": "Name",
6459
+ "marketing.landing.field.phone": "Phone",
6460
+ "marketing.landing.field.email": "Email",
6461
+ "marketing.landing.field.document": "Tax ID",
6462
+ "marketing.landing.field.notes": "Note",
6463
+ "marketing.landing.minAdvance": "Minimum notice (hours)",
6464
+ "marketing.landing.minAdvanceHint": "Stops someone booking for ten minutes from now.",
6465
+ "marketing.landing.maxAdvance": "Furthest ahead (days)",
6466
+ "marketing.landing.maxAdvanceHint": "Stops someone booking for next year.",
6467
+ "marketing.landing.publicKey": "Public key: {{key}} \u2014 it never changes once created, because a customer may have saved it."
6124
6468
  };
6125
6469
 
6126
6470
  // src/locales/pt-BR.ts
@@ -6324,7 +6668,48 @@ var ptBR = {
6324
6668
  "marketing.content.status.recording": "grava\xE7\xE3o",
6325
6669
  "marketing.content.status.editing": "edi\xE7\xE3o",
6326
6670
  "marketing.content.status.scheduled": "agendado",
6327
- "marketing.content.status.published": "publicado"
6671
+ "marketing.content.status.published": "publicado",
6672
+ "marketing.brand.title": "Marca",
6673
+ "marketing.brand.subtitle": "O que o assistente l\xEA antes de escrever qualquer texto para esta casa.",
6674
+ "marketing.brand.identity": "Identidade",
6675
+ "marketing.brand.name": "Nome da marca",
6676
+ "marketing.brand.tagline": "Assinatura",
6677
+ "marketing.brand.mission": "Posicionamento",
6678
+ "marketing.brand.voice": "Como a marca fala",
6679
+ "marketing.brand.voiceHint": "Isto acompanha toda pergunta feita ao assistente enquanto o marketing est\xE1 aberto.",
6680
+ "marketing.brand.tone": "Tom de voz",
6681
+ "marketing.brand.tonePlaceholder": "Direto e caloroso. Fala como gente, n\xE3o como folheto.",
6682
+ "marketing.brand.keywords": "Palavras que a casa usa",
6683
+ "marketing.brand.ctas": "Chamadas padr\xE3o",
6684
+ "marketing.brand.ctasPlaceholder": "Agende seu hor\xE1rio, Fale no WhatsApp",
6685
+ "marketing.brand.hashtags": "Hashtags",
6686
+ "marketing.brand.avoid": "O que a marca n\xE3o diz",
6687
+ "marketing.brand.avoidHint": "\xC9 o \xFAnico campo cujo esquecimento \xE9 invis\xEDvel: ningu\xE9m revisa uma pe\xE7a procurando a palavra que a casa pediu para n\xE3o usar.",
6688
+ "marketing.brand.avoidWords": "Palavras a evitar",
6689
+ "marketing.brand.avoidPlaceholder": "barato, imperd\xEDvel, promo\xE7\xE3o rel\xE2mpago",
6690
+ "marketing.brand.agentOn": "O assistente est\xE1 recebendo estes padr\xF5es e vai segui-los ao escrever.",
6691
+ "marketing.brand.agentOff": "Nada preenchido ainda \u2014 o assistente escreve sem orienta\xE7\xE3o de marca.",
6692
+ "marketing.brand.agentSummary": "O kit de marca da casa: tom de voz, palavras-chave, chamadas padr\xE3o e as palavras proibidas.",
6693
+ "marketing.brand.saved": "Marca salva.",
6694
+ "marketing.brand.saveFailed": "N\xE3o foi poss\xEDvel salvar a marca.",
6695
+ "marketing.landing.formTitle": "O formul\xE1rio da p\xE1gina",
6696
+ "marketing.landing.formHint": "A p\xE1gina \xE9 o conte\xFAdo; isto \xE9 a regra da porta \u2014 o que ela pede e para onde leva.",
6697
+ "marketing.landing.destination": "Para onde leva",
6698
+ "marketing.landing.dest.lead": "Captura contato",
6699
+ "marketing.landing.dest.booking": "Marca hor\xE1rio",
6700
+ "marketing.landing.dest.reservation": "Marca mesa",
6701
+ "marketing.landing.required": "O que a casa exige",
6702
+ "marketing.landing.requiredHint": "Cada campo exigido a mais derruba convers\xE3o. Pe\xE7a o m\xEDnimo para conseguir responder.",
6703
+ "marketing.landing.field.name": "Nome",
6704
+ "marketing.landing.field.phone": "Telefone",
6705
+ "marketing.landing.field.email": "E-mail",
6706
+ "marketing.landing.field.document": "CPF",
6707
+ "marketing.landing.field.notes": "Observa\xE7\xE3o",
6708
+ "marketing.landing.minAdvance": "Anteced\xEAncia m\xEDnima (horas)",
6709
+ "marketing.landing.minAdvanceHint": "Impede marcar para daqui a dez minutos.",
6710
+ "marketing.landing.maxAdvance": "Prazo m\xE1ximo (dias)",
6711
+ "marketing.landing.maxAdvanceHint": "Impede marcar para o ano que vem.",
6712
+ "marketing.landing.publicKey": "Chave p\xFAblica: {{key}} \u2014 n\xE3o muda depois de criada, porque o cliente pode t\xEA-la salvo."
6328
6713
  };
6329
6714
 
6330
6715
  // src/locales/index.ts