@fayz-ai/plugin-marketing 0.12.1 → 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.
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/types.d.ts +6 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/index.js +393 -8
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/LandingPageComposer.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/data/mock.d.ts.map
CHANGED
|
@@ -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;
|
|
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;
|
|
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"}
|
package/dist/data/types.d.ts
CHANGED
|
@@ -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>;
|
package/dist/data/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/data/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,aAAa,
|
|
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
|
-
|
|
3376
|
-
|
|
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
|