mitre-actions-widget 0.1.8 → 2.2.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/README.md CHANGED
@@ -45,27 +45,24 @@ const products = JSON.parse(process.env.VITE_PRODUCT_ID!);
45
45
 
46
46
  <MitreActionsWidget
47
47
  products={products}
48
- apiUrl={process.env.VITE_REGISTER_LEADS_URL!}
49
- apiToken={process.env.VITE_REGISTER_LEADS_TOKEN!}
48
+ environment="prod"
50
49
  />
51
50
 
52
51
  // Com cor personalizada
53
52
  <MitreActionsWidget
54
53
  products={products}
55
- apiUrl={process.env.VITE_REGISTER_LEADS_URL!}
56
- apiToken={process.env.VITE_REGISTER_LEADS_TOKEN!}
54
+ environment="prod"
57
55
  backgroundColor="#FF5733"
58
56
  />
59
57
 
60
58
  // Com customização do formulário e callback de sucesso (props opcionais)
61
59
  <MitreActionsWidget
62
60
  products={products}
63
- apiUrl={process.env.VITE_REGISTER_LEADS_URL!}
64
- apiToken={process.env.VITE_REGISTER_LEADS_TOKEN!}
61
+ environment="prod"
65
62
  // formShowHeader={true}
66
63
  // formTitle="Fale conosco"
67
64
  // formSubtitle="Preencha o formulário abaixo"
68
- // formShowPreferenciaContato={true}
65
+ // formShowContactPreference={true}
69
66
  // onFormSuccess={(body, leadId) => console.log("Lead criado:", leadId)}
70
67
  />
71
68
  ```
@@ -127,13 +124,12 @@ Depois de instalar a biblioteca, você pode começar a usá-la diretamente no se
127
124
  O `MitreActionsWidget` aceita as seguintes props:
128
125
 
129
126
  - **`products`** (Product[]): Array de produtos do empreendimento.
130
- - **`apiUrl`** (string): URL da API para registro dos leads.
131
- - **`apiToken`** (string): Token de autenticação da API.
127
+ - **`environment`** (`"staging" | "prod"`, opcional): Define de qual config interno do `mitre-form-component` o widget lê `apiUrl`, `apiToken`, `whatsappPhone` e `chatUrl`. Padrão: `"staging"`.
132
128
  - **`backgroundColor`** (string, opcional): Cor de fundo personalizada para o botão flutuante. Padrão: `#2DCE68` (verde).
133
129
  - **`formShowHeader`** (boolean, opcional): Exibe ou oculta o cabeçalho do formulário.
134
130
  - **`formTitle`** (string, opcional): Título exibido no cabeçalho do formulário.
135
131
  - **`formSubtitle`** (string, opcional): Subtítulo exibido no cabeçalho do formulário.
136
- - **`formShowPreferenciaContato`** (boolean, opcional): Exibe ou oculta o campo de preferência de contato no formulário.
132
+ - **`formShowContactPreference`** (boolean, opcional): Exibe ou oculta o campo de preferência de contato no formulário.
137
133
  - **`onFormSuccess`** ((requestBody, leadId) => void, opcional): Callback chamado após o envio bem-sucedido do formulário. Recebe o corpo da requisição (`RequestBody`) e o ID do lead gerado.
138
134
 
139
135
  ---
@@ -160,13 +156,12 @@ const products = JSON.parse(process.env.NEXT_PUBLIC_PRODUCT_ID!);
160
156
  <ErrorBoundary fallback={<div>Erro ao carregar o formulário</div>}>
161
157
  <MitreActionsWidget
162
158
  products={products}
163
- apiUrl={process.env.NEXT_PUBLIC_REGISTER_LEADS_URL!}
164
- apiToken={process.env.NEXT_PUBLIC_REGISTER_LEADS_TOKEN!}
159
+ environment="prod"
165
160
  backgroundColor="#2DCE68" // Opcional: cor personalizada
166
161
  // formShowHeader={true}
167
162
  // formTitle="Fale conosco"
168
163
  // formSubtitle="Preencha o formulário abaixo"
169
- // formShowPreferenciaContato={true}
164
+ // formShowContactPreference={true}
170
165
  // onFormSuccess={(body, leadId) => console.log("Lead criado:", leadId)}
171
166
  />
172
167
  </ErrorBoundary>;
package/dist/index.cjs CHANGED
@@ -35,7 +35,7 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/components/MitreActionsWidget/index.tsx
38
- var import_react5 = __toESM(require("react"), 1);
38
+ var import_react4 = __toESM(require("react"), 1);
39
39
 
40
40
  // src/components/styles/utils.ts
41
41
  function flex(direction = "row", alignItems, justifyContent) {
@@ -184,7 +184,7 @@ var ensureMontserratFont = () => {
184
184
  };
185
185
 
186
186
  // src/components/Selector/index.tsx
187
- var import_react4 = require("react");
187
+ var import_react3 = require("react");
188
188
 
189
189
  // src/components/hooks/useClickOutside.ts
190
190
  var import_react = require("react");
@@ -502,95 +502,6 @@ var Navbar = ({ setCurrentOpen, currentOpen }) => {
502
502
  };
503
503
  var Navbar_default = Navbar;
504
504
 
505
- // src/components/TalkWithASeller/index.tsx
506
- var import_react2 = require("react");
507
-
508
- // src/components/utils/utm.ts
509
- var UTM_STORAGE_KEY = "utm_meta";
510
- var TTL_DAYS = 7;
511
- var isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
512
- var daysToMs = (days) => days * 24 * 60 * 60 * 1e3;
513
- function readStoredUtm(maxAgeDays = TTL_DAYS) {
514
- if (!isBrowser()) return { data: null, isValid: false };
515
- try {
516
- const raw = window.localStorage.getItem(UTM_STORAGE_KEY);
517
- if (!raw) return { data: null, isValid: false };
518
- const parsed = JSON.parse(raw);
519
- if (!parsed?.createdAt) return { data: null, isValid: false };
520
- const age = Date.now() - new Date(parsed.createdAt).getTime();
521
- const isValid = age <= daysToMs(maxAgeDays);
522
- return { data: isValid ? parsed : null, isValid };
523
- } catch {
524
- return { data: null, isValid: false };
525
- }
526
- }
527
- function saveUtm(data, now = /* @__PURE__ */ new Date()) {
528
- if (!isBrowser()) return null;
529
- const payload = {
530
- utm_source: data.utm_source || "direto",
531
- utm_campaign: data.utm_campaign,
532
- utm_medium: data.utm_medium,
533
- utm_term: data.utm_term,
534
- createdAt: now.toISOString()
535
- };
536
- try {
537
- window.localStorage.setItem(UTM_STORAGE_KEY, JSON.stringify(payload));
538
- } catch {
539
- }
540
- return payload;
541
- }
542
- function parseUrlUtm(loc) {
543
- if (!isBrowser()) return {};
544
- const locationObj = loc || window.location;
545
- const sp = new URL(locationObj.href).searchParams;
546
- const utm_source = sp.get("utm_source") || void 0;
547
- const utm_campaign = sp.get("utm_campaign") || void 0;
548
- const utm_medium = sp.get("utm_medium") || void 0;
549
- const utm_term = sp.get("utm_term") || void 0;
550
- return { utm_source, utm_campaign, utm_medium, utm_term };
551
- }
552
- function inferSourceFromReferrer(ref) {
553
- if (!isBrowser()) return void 0;
554
- const href = (ref ?? document.referrer ?? "").toLowerCase();
555
- if (!href) return "direto";
556
- const hostMatch = href.match(/https?:\/\/([^/]+)/);
557
- const host = hostMatch?.[1] ?? href;
558
- if (/google\./.test(host) || /^g\.co$/.test(host) || /^goo\.gl$/.test(host)) return "google";
559
- if (/(^|\.)facebook\.com$/.test(host) || /(^|\.)m\.facebook\.com$/.test(host) || /(^|\.)fb\.com$/.test(host) || /(^|\.)fb\.me$/.test(host) || /(^|\.)fb\.watch$/.test(host) || /(^|\.)m\.me$/.test(host)) return "facebook";
560
- if (/(^|\.)instagram\.com$/.test(host) || /(^|\.)l\.instagram\.com$/.test(host) || /(^|\.)instagr\.am$/.test(host) || /^ig\.me$/.test(host)) return "instagram";
561
- if (/(^|\.)linkedin\.com$/.test(host) || /^lnkd\.in$/.test(host)) return "linkedin";
562
- if (/^t\.co$/.test(host) || /(^|\.)twitter\.com$/.test(host) || /(^|\.)x\.com$/.test(host)) return "twitter";
563
- if (/(^|\.)youtube\.com$/.test(host) || /^youtu\.be$/.test(host)) return "youtube";
564
- if (/(^|\.)mail\.google\.com$/.test(host)) return "email";
565
- if (/(^|\.)outlook\.live\.com$/.test(host) || /(^|\.)outlook\.office\.com$/.test(host)) return "email";
566
- if (/^(wa\.me)$/.test(host) || /(^|\.)api\.whatsapp\.com$/.test(host) || /(^|\.)web\.whatsapp\.com$/.test(host)) return "whatsapp";
567
- if (/^t\.me$/.test(host)) return "telegram";
568
- if (/(^|\.)tiktok\.com$/.test(host)) return "tiktok";
569
- return "direto";
570
- }
571
- function resolveUtmWithPriority(now = /* @__PURE__ */ new Date(), propOverride) {
572
- const stored = readStoredUtm();
573
- if (stored.data) {
574
- const merged = { ...stored.data, ...propOverride };
575
- return { data: merged, source: "localStorage" };
576
- }
577
- const fromUrl = parseUrlUtm();
578
- if (fromUrl.utm_source) {
579
- const saved = saveUtm(fromUrl, now);
580
- const merged = { ...saved, ...propOverride };
581
- return { data: merged, source: "url" };
582
- }
583
- const utm_source = inferSourceFromReferrer();
584
- const payload = {
585
- utm_source: utm_source || "direto",
586
- utm_campaign: propOverride?.utm_campaign,
587
- utm_medium: propOverride?.utm_medium,
588
- utm_term: propOverride?.utm_term,
589
- createdAt: now.toISOString()
590
- };
591
- return { data: payload, source: "referrer" };
592
- }
593
-
594
505
  // src/components/styles.ts
595
506
  var import_styled_components5 = __toESM(require("styled-components"), 1);
596
507
  var Container2 = import_styled_components5.default.div`
@@ -673,113 +584,13 @@ var SelectorOption = import_styled_components5.default.div`
673
584
  }
674
585
  `;
675
586
 
676
- // src/components/utils/windowPopup.ts
677
- var openWindowPopup = (url) => {
678
- const dualScreenLeft = window.screenLeft !== void 0 ? window.screenLeft : window.screenX;
679
- const dualScreenTop = window.screenTop !== void 0 ? window.screenTop : window.screenY;
680
- const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
681
- const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
682
- const systemZoom = width / window.screen.availWidth;
683
- const w = 674;
684
- const h = 540;
685
- const left = (width - w) / 2 / systemZoom + dualScreenLeft;
686
- const top = (height - h) / 2 / systemZoom + dualScreenTop;
687
- window.open(
688
- url,
689
- `mitre-window`,
690
- `width=${w / systemZoom},
691
- height=${h / systemZoom},
692
- top=${top},
693
- left=${left},
694
- status=no,
695
- toolbar=no,
696
- location=no,
697
- menubar=no,
698
- resizable=no,
699
- fullscreen=no`
700
- );
701
- };
702
-
703
587
  // src/components/TalkWithASeller/index.tsx
704
588
  var import_jsx_runtime2 = require("react/jsx-runtime");
705
- var TalkWithSeller = ({
706
- products,
707
- apiUrl,
708
- setCurrentFormOpen
709
- }) => {
710
- const [utm, setUtm] = (0, import_react2.useState)({ utm_source: "direto", createdAt: (/* @__PURE__ */ new Date()).toISOString() });
711
- (0, import_react2.useEffect)(() => {
712
- if (!isBrowser()) return;
713
- const { data } = resolveUtmWithPriority(/* @__PURE__ */ new Date());
714
- setUtm(data);
715
- }, []);
716
- const validateProducts = () => {
717
- if (!products) {
718
- return "Lista de produtos n\xE3o foi fornecida";
719
- }
720
- if (!Array.isArray(products)) {
721
- return "Lista de produtos deve ser um array";
722
- }
723
- if (products.length === 0) {
724
- return "Lista de produtos n\xE3o pode estar vazia";
725
- }
726
- for (let i = 0; i < products.length; i++) {
727
- const product = products[i];
728
- if (!product) {
729
- return `Produto na posi\xE7\xE3o ${i} \xE9 inv\xE1lido (nulo/undefined)`;
730
- }
731
- if (typeof product.id !== "number" || product.id <= 0) {
732
- return `Produto na posi\xE7\xE3o ${i} possui ID inv\xE1lido (deve ser um n\xFAmero positivo)`;
733
- }
734
- if (typeof product.name !== "string" || product.name.trim().length === 0) {
735
- return `Produto na posi\xE7\xE3o ${i} possui nome inv\xE1lido (deve ser uma string n\xE3o vazia)`;
736
- }
737
- }
738
- return null;
739
- };
740
- const onWhatsClick = async () => {
741
- try {
742
- const validationError = validateProducts();
743
- if (validationError) {
744
- console.log(validationError);
745
- throw new Error("Missing required parameters");
746
- }
747
- const params = new URLSearchParams();
748
- params.append("id_produto", products[0].id.toString());
749
- params.append("utm_source", utm.utm_source);
750
- if (utm.utm_medium) params.append("utm_medium", utm.utm_medium);
751
- if (utm.utm_campaign) params.append("utm_campaign", utm.utm_campaign);
752
- if (utm.utm_term) params.append("utm_term", utm.utm_term);
753
- params.append("canal", "whatsapp");
754
- const url = `${apiUrl}/atendimento?${params.toString()}`;
755
- openWindowPopup(url);
756
- } catch (error) {
757
- console.error("Error in onWhatsClick:", error);
758
- }
759
- };
760
- const onChatClick = async () => {
761
- try {
762
- const validationError = validateProducts();
763
- if (validationError) {
764
- console.log(validationError);
765
- throw new Error("Missing required parameters");
766
- }
767
- const params = new URLSearchParams();
768
- params.append("id_produto", products[0].id.toString());
769
- params.append("utm_source", utm.utm_source);
770
- if (utm.utm_medium) params.append("utm_medium", utm.utm_medium);
771
- if (utm.utm_campaign) params.append("utm_campaign", utm.utm_campaign);
772
- if (utm.utm_term) params.append("utm_term", utm.utm_term);
773
- const url = `${apiUrl}/atendimento?${params.toString()}`;
774
- openWindowPopup(url);
775
- } catch (error) {
776
- console.error("Error in onChatClick:", error);
777
- }
778
- };
589
+ var TalkWithSeller = ({ openForm }) => {
779
590
  const itens = [
780
- { title: "Atendimento por Whatsapp", text: "Canal exclusivo para vendas", onClick: () => onWhatsClick(), icon: "whatsapp" },
781
- { title: "Atendimento por chat", text: "Canal exclusivo para vendas", onClick: () => onChatClick(), icon: "chat" },
782
- { title: "Atendimento por mensagem", text: "Canal exclusivo para vendas", onClick: () => setCurrentFormOpen(0), icon: "message" }
591
+ { title: "Atendimento por Whatsapp", text: "Canal exclusivo para vendas", onClick: () => openForm("whatsapp"), icon: "whatsapp" },
592
+ { title: "Atendimento por chat", text: "Canal exclusivo para vendas", onClick: () => openForm("chat"), icon: "chat" },
593
+ { title: "Atendimento por mensagem", text: "Canal exclusivo para vendas", onClick: () => openForm("form"), icon: "message" }
783
594
  ];
784
595
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
785
596
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Title, { children: "Converse com um corretor" }),
@@ -805,10 +616,10 @@ var TalkWithSeller = ({
805
616
  var TalkWithASeller_default = TalkWithSeller;
806
617
 
807
618
  // src/components/hooks/useDeviceType.ts
808
- var import_react3 = require("react");
619
+ var import_react2 = require("react");
809
620
  function useDeviceType() {
810
- const [deviceType, setDeviceType] = (0, import_react3.useState)("Unknown");
811
- (0, import_react3.useEffect)(() => {
621
+ const [deviceType, setDeviceType] = (0, import_react2.useState)("Unknown");
622
+ (0, import_react2.useEffect)(() => {
812
623
  const userAgent = navigator.userAgent || navigator.vendor;
813
624
  if (/android/i.test(userAgent)) {
814
625
  setDeviceType("Android");
@@ -952,18 +763,19 @@ var import_react_error_boundary = require("react-error-boundary");
952
763
  var import_jsx_runtime5 = require("react/jsx-runtime");
953
764
  var Selector = ({
954
765
  products,
955
- apiUrl,
956
- apiToken,
766
+ environment,
957
767
  close,
958
768
  formShowHeader,
959
769
  formTitle,
960
770
  formSubtitle,
961
- formShowPreferenciaContato,
771
+ formShowContactPreference,
962
772
  onFormSuccess
963
773
  }) => {
964
- const ref = (0, import_react4.useRef)(null);
965
- const [currentOpen, setCurrentOpen] = (0, import_react4.useState)(0);
966
- const [currentFormOpen, setCurrentFormOpen] = (0, import_react4.useState)(-1);
774
+ const ref = (0, import_react3.useRef)(null);
775
+ const [currentOpen, setCurrentOpen] = (0, import_react3.useState)(0);
776
+ const [formChannel, setFormChannel] = (0, import_react3.useState)(null);
777
+ const openForm = (channel) => setFormChannel(channel);
778
+ const closeForm = () => setFormChannel(null);
967
779
  useClickOutside_default(ref, close);
968
780
  const tabs = [
969
781
  TalkWithASeller_default,
@@ -978,9 +790,9 @@ var Selector = ({
978
790
  import_hi.HiArrowNarrowLeft,
979
791
  {
980
792
  className: "back-icon",
981
- onClick: () => setCurrentFormOpen(-1),
793
+ onClick: closeForm,
982
794
  size: 28,
983
- visibility: currentFormOpen >= 0 ? "visible" : "hidden",
795
+ visibility: formChannel !== null ? "visible" : "hidden",
984
796
  color: colors.black
985
797
  }
986
798
  ),
@@ -994,7 +806,7 @@ var Selector = ({
994
806
  }
995
807
  )
996
808
  ] }),
997
- currentFormOpen >= 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FormContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_error_boundary.ErrorBoundary, { fallback: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(FormErrorContainer, { children: [
809
+ formChannel !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FormContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_error_boundary.ErrorBoundary, { fallback: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(FormErrorContainer, { children: [
998
810
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
999
811
  import_hi.HiOutlineExclamation,
1000
812
  {
@@ -1007,25 +819,18 @@ var Selector = ({
1007
819
  import_mitre_form_component.MitreFormComponent,
1008
820
  {
1009
821
  products,
1010
- apiUrl,
1011
- apiToken,
822
+ environment,
823
+ channel: formChannel,
1012
824
  backgroundColor: "transparent",
1013
825
  innerPadding: "0",
1014
826
  showHeader: formShowHeader,
1015
827
  title: formTitle,
1016
828
  subtitle: formSubtitle,
1017
- showPreferenciaContato: formShowPreferenciaContato,
829
+ showContactPreference: formShowContactPreference,
1018
830
  onSuccess: onFormSuccess
1019
831
  }
1020
832
  ) }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1021
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ContentContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1022
- CurrentComponent,
1023
- {
1024
- setCurrentFormOpen,
1025
- products,
1026
- apiUrl
1027
- }
1028
- ) }),
833
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ContentContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CurrentComponent, { openForm }) }),
1029
834
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FooterContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1030
835
  Navbar_default,
1031
836
  {
@@ -1040,19 +845,18 @@ var Selector_default = Selector;
1040
845
 
1041
846
  // src/components/MitreActionsWidget/index.tsx
1042
847
  var import_jsx_runtime6 = require("react/jsx-runtime");
1043
- var MitreActionsWidget = import_react5.default.forwardRef(({
848
+ var MitreActionsWidget = import_react4.default.forwardRef(({
1044
849
  products,
1045
- apiUrl,
1046
- apiToken,
850
+ environment,
1047
851
  backgroundColor,
1048
852
  formShowHeader,
1049
853
  formTitle,
1050
854
  formSubtitle,
1051
- formShowPreferenciaContato,
855
+ formShowContactPreference,
1052
856
  onFormSuccess
1053
857
  }, ref) => {
1054
- const [open, setOpen] = (0, import_react5.useState)(false);
1055
- (0, import_react5.useEffect)(() => {
858
+ const [open, setOpen] = (0, import_react4.useState)(false);
859
+ (0, import_react4.useEffect)(() => {
1056
860
  ensureMontserratFont();
1057
861
  }, []);
1058
862
  function handleOpen() {
@@ -1062,7 +866,7 @@ var MitreActionsWidget = import_react5.default.forwardRef(({
1062
866
  setOpen(false);
1063
867
  }
1064
868
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ComponentWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Container, { children: [
1065
- open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Selector_default, { products, apiUrl, apiToken, close: () => close(), formShowHeader, formTitle, formSubtitle, formShowPreferenciaContato, onFormSuccess }),
869
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Selector_default, { products, environment, close: () => close(), formShowHeader, formTitle, formSubtitle, formShowContactPreference, onFormSuccess }),
1066
870
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ButtonContainer, { ref, open, $backgroundColor: backgroundColor, onClick: () => handleOpen(), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1067
871
  "img",
1068
872
  {