@greatapps/common 1.1.778 → 1.1.779

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.
@@ -21,52 +21,49 @@ function DisableTwoFactorAuthModal({
21
21
  }) {
22
22
  const translate = useTranslations();
23
23
  const { code, setCode, isValid, isLoading, handleClose, handleSubmit } = useDisableTwoFactorAuthForm({ onClose, onSuccess });
24
- return (
25
- // Abre por cima do modal de configurações da conta, que é irmão na árvore.
26
- /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsx(DialogContent, { className: "flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-117.5 lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]", children: /* @__PURE__ */ jsxs(
27
- "form",
28
- {
29
- onSubmit: (e) => {
30
- e.preventDefault();
31
- handleSubmit();
32
- },
33
- className: "flex flex-col flex-1 lg:flex-none",
34
- children: [
35
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:gap-5 p-4 lg:p-5 flex-1 lg:flex-none", children: [
36
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-fit bg-gray-50 rounded-lg p-2", children: /* @__PURE__ */ jsx(IconShieldCheck, { size: 20, className: "text-gray-600" }) }),
37
- /* @__PURE__ */ jsxs(DialogHeader, { className: "p-0 gap-2", children: [
38
- /* @__PURE__ */ jsx(DialogTitle, { className: "paragraph-medium-semibold text-gray-950", children: translate("common.account.twoFactor.disableTitle") }),
39
- /* @__PURE__ */ jsx(DialogDescription, { className: "paragraph-small-regular text-gray-600", children: translate("common.account.twoFactor.disableDescription") })
40
- ] }),
41
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
42
- /* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-gray-700", children: translate("common.account.twoFactor.authenticatorCode") }),
43
- /* @__PURE__ */ jsx(InputOTP, { maxLength: 6, pattern: REGEXP_ONLY_DIGITS, value: code, onChange: setCode, children: /* @__PURE__ */ jsx(InputOTPGroup, { className: "gap-2", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(
44
- InputOTPSlot,
45
- {
46
- index: i,
47
- className: "w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200"
48
- },
49
- i
50
- )) }) })
51
- ] })
24
+ return /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsx(DialogContent, { className: "flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-117.5 lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]", children: /* @__PURE__ */ jsxs(
25
+ "form",
26
+ {
27
+ onSubmit: (e) => {
28
+ e.preventDefault();
29
+ handleSubmit();
30
+ },
31
+ className: "flex flex-col flex-1 lg:flex-none",
32
+ children: [
33
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:gap-5 p-4 lg:p-5 flex-1 lg:flex-none", children: [
34
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-fit bg-gray-50 rounded-lg p-2", children: /* @__PURE__ */ jsx(IconShieldCheck, { size: 20, className: "text-gray-600" }) }),
35
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "p-0 gap-2", children: [
36
+ /* @__PURE__ */ jsx(DialogTitle, { className: "paragraph-medium-semibold text-gray-950", children: translate("common.account.twoFactor.disableTitle") }),
37
+ /* @__PURE__ */ jsx(DialogDescription, { className: "paragraph-small-regular text-gray-600", children: translate("common.account.twoFactor.disableDescription") })
52
38
  ] }),
53
- /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center gap-2 p-4 lg:p-5 border-t border-gray-200 mt-auto lg:mt-0", children: [
54
- /* @__PURE__ */ jsx(Button, { className: "h-10! flex-1", disabled: !isValid || isLoading, type: "submit", children: isLoading ? translate("common.account.twoFactor.disabling") : translate("common.account.twoFactor.disable") }),
55
- /* @__PURE__ */ jsx(
56
- Button,
39
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
40
+ /* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-gray-700", children: translate("common.account.twoFactor.authenticatorCode") }),
41
+ /* @__PURE__ */ jsx(InputOTP, { maxLength: 6, pattern: REGEXP_ONLY_DIGITS, value: code, onChange: setCode, children: /* @__PURE__ */ jsx(InputOTPGroup, { className: "gap-2", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(
42
+ InputOTPSlot,
57
43
  {
58
- variant: "secondary",
59
- className: "h-10! flex-1",
60
- type: "button",
61
- onClick: handleClose,
62
- children: translate("common.actions.cancel")
63
- }
64
- )
44
+ index: i,
45
+ className: "w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200"
46
+ },
47
+ i
48
+ )) }) })
65
49
  ] })
66
- ]
67
- }
68
- ) }) }) })
69
- );
50
+ ] }),
51
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center gap-2 p-4 lg:p-5 border-t border-gray-200 mt-auto lg:mt-0", children: [
52
+ /* @__PURE__ */ jsx(Button, { className: "h-10! flex-1", disabled: !isValid || isLoading, type: "submit", children: isLoading ? translate("common.account.twoFactor.disabling") : translate("common.account.twoFactor.disable") }),
53
+ /* @__PURE__ */ jsx(
54
+ Button,
55
+ {
56
+ variant: "secondary",
57
+ className: "h-10! flex-1",
58
+ type: "button",
59
+ onClick: handleClose,
60
+ children: translate("common.actions.cancel")
61
+ }
62
+ )
63
+ ] })
64
+ ]
65
+ }
66
+ ) }) }) });
70
67
  }
71
68
  export {
72
69
  DisableTwoFactorAuthModal as default
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/account/DisableTwoFactorAuthModal.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { IconShieldCheck } from '@tabler/icons-react';\nimport { Button } from '../ui/buttons/Button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../ui/overlay/Dialog';\nimport { DialogLayer } from '../ui/overlay/layers';\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from '../ui/form/InputOtp';\nimport { REGEXP_ONLY_DIGITS } from 'input-otp';\nimport useDisableTwoFactorAuthForm from './hooks/useDisableTwoFactorAuthForm';\n\ninterface DisableTwoFactorAuthModalProps {\n open: boolean;\n onClose: () => void;\n onSuccess?: () => void;\n}\n\nexport default function DisableTwoFactorAuthModal({\n open,\n onClose,\n onSuccess,\n}: DisableTwoFactorAuthModalProps) {\n const translate = useTranslations();\n const { code, setCode, isValid, isLoading, handleClose, handleSubmit } =\n useDisableTwoFactorAuthForm({ onClose, onSuccess });\n\n return (\n // Abre por cima do modal de configurações da conta, que é irmão na árvore.\n <DialogLayer>\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-117.5 lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]\">\n <form\n onSubmit={(e) => {\n e.preventDefault();\n handleSubmit();\n }}\n className=\"flex flex-col flex-1 lg:flex-none\"\n >\n <div className=\"flex flex-col gap-4 lg:gap-5 p-4 lg:p-5 flex-1 lg:flex-none\">\n <div className=\"flex items-center justify-center w-fit bg-gray-50 rounded-lg p-2\">\n <IconShieldCheck size={20} className=\"text-gray-600\" />\n </div>\n <DialogHeader className=\"p-0 gap-2\">\n <DialogTitle className=\"paragraph-medium-semibold text-gray-950\">\n {translate('common.account.twoFactor.disableTitle')}\n </DialogTitle>\n <DialogDescription className=\"paragraph-small-regular text-gray-600\">\n {translate('common.account.twoFactor.disableDescription')}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"flex flex-col gap-2\">\n <span className=\"paragraph-small-medium text-gray-700\">\n {translate('common.account.twoFactor.authenticatorCode')}\n </span>\n <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS} value={code} onChange={setCode}>\n <InputOTPGroup className=\"gap-2\">\n {Array.from({ length: 6 }).map((_, i) => (\n <InputOTPSlot\n key={i}\n index={i}\n className=\"w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200\"\n />\n ))}\n </InputOTPGroup>\n </InputOTP>\n </div>\n </div>\n\n <div className=\"flex flex-row items-center gap-2 p-4 lg:p-5 border-t border-gray-200 mt-auto lg:mt-0\">\n <Button className=\"h-10! flex-1\" disabled={!isValid || isLoading} type=\"submit\">\n {isLoading\n ? translate('common.account.twoFactor.disabling')\n : translate('common.account.twoFactor.disable')}\n </Button>\n <Button\n variant=\"secondary\"\n className=\"h-10! flex-1\"\n type=\"button\"\n onClick={handleClose}\n >\n {translate('common.actions.cancel')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n );\n}\n"],"mappings":";AA8CgB,cAEF,YAFE;AA5ChB,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,UAAU,eAAe,oBAAoB;AACtD,SAAS,0BAA0B;AACnC,OAAO,iCAAiC;AAQzB,SAAR,0BAA2C;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,YAAY,gBAAgB;AAClC,QAAM,EAAE,MAAM,SAAS,SAAS,WAAW,aAAa,aAAa,IACnE,4BAA4B,EAAE,SAAS,UAAU,CAAC;AAEpD;AAAA;AAAA,IAEE,oBAAC,eACC,8BAAC,UAAO,MAAY,cAAc,aAChC,8BAAC,iBAAc,WAAU,+SACvB;AAAA,MAAC;AAAA;AAAA,QACC,UAAU,CAAC,MAAM;AACf,YAAE,eAAe;AACjB,uBAAa;AAAA,QACf;AAAA,QACA,WAAU;AAAA,QAEV;AAAA,+BAAC,SAAI,WAAU,+DACb;AAAA,gCAAC,SAAI,WAAU,oEACb,8BAAC,mBAAgB,MAAM,IAAI,WAAU,iBAAgB,GACvD;AAAA,YACA,qBAAC,gBAAa,WAAU,aACtB;AAAA,kCAAC,eAAY,WAAU,2CACpB,oBAAU,uCAAuC,GACpD;AAAA,cACA,oBAAC,qBAAkB,WAAU,yCAC1B,oBAAU,6CAA6C,GAC1D;AAAA,eACF;AAAA,YAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,kCAAC,UAAK,WAAU,wCACb,oBAAU,4CAA4C,GACzD;AAAA,cACA,oBAAC,YAAS,WAAW,GAAG,SAAS,oBAAoB,OAAO,MAAM,UAAU,SAC1E,8BAAC,iBAAc,WAAU,SACtB,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC;AAAA,gBAAC;AAAA;AAAA,kBAEC,OAAO;AAAA,kBACP,WAAU;AAAA;AAAA,gBAFL;AAAA,cAGP,CACD,GACH,GACF;AAAA,eACF;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,wFACb;AAAA,gCAAC,UAAO,WAAU,gBAAe,UAAU,CAAC,WAAW,WAAW,MAAK,UACpE,sBACG,UAAU,oCAAoC,IAC9C,UAAU,kCAAkC,GAClD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACV,MAAK;AAAA,gBACL,SAAS;AAAA,gBAER,oBAAU,uBAAuB;AAAA;AAAA,YACpC;AAAA,aACF;AAAA;AAAA;AAAA,IACF,GACF,GACF,GACF;AAAA;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/account/DisableTwoFactorAuthModal.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { IconShieldCheck } from '@tabler/icons-react';\nimport { Button } from '../ui/buttons/Button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../ui/overlay/Dialog';\nimport { DialogLayer } from '../ui/overlay/layers';\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from '../ui/form/InputOtp';\nimport { REGEXP_ONLY_DIGITS } from 'input-otp';\nimport useDisableTwoFactorAuthForm from './hooks/useDisableTwoFactorAuthForm';\n\ninterface DisableTwoFactorAuthModalProps {\n open: boolean;\n onClose: () => void;\n onSuccess?: () => void;\n}\n\nexport default function DisableTwoFactorAuthModal({\n open,\n onClose,\n onSuccess,\n}: DisableTwoFactorAuthModalProps) {\n const translate = useTranslations();\n const { code, setCode, isValid, isLoading, handleClose, handleSubmit } =\n useDisableTwoFactorAuthForm({ onClose, onSuccess });\n\n return (\n <DialogLayer>\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-117.5 lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]\">\n <form\n onSubmit={(e) => {\n e.preventDefault();\n handleSubmit();\n }}\n className=\"flex flex-col flex-1 lg:flex-none\"\n >\n <div className=\"flex flex-col gap-4 lg:gap-5 p-4 lg:p-5 flex-1 lg:flex-none\">\n <div className=\"flex items-center justify-center w-fit bg-gray-50 rounded-lg p-2\">\n <IconShieldCheck size={20} className=\"text-gray-600\" />\n </div>\n <DialogHeader className=\"p-0 gap-2\">\n <DialogTitle className=\"paragraph-medium-semibold text-gray-950\">\n {translate('common.account.twoFactor.disableTitle')}\n </DialogTitle>\n <DialogDescription className=\"paragraph-small-regular text-gray-600\">\n {translate('common.account.twoFactor.disableDescription')}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"flex flex-col gap-2\">\n <span className=\"paragraph-small-medium text-gray-700\">\n {translate('common.account.twoFactor.authenticatorCode')}\n </span>\n <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS} value={code} onChange={setCode}>\n <InputOTPGroup className=\"gap-2\">\n {Array.from({ length: 6 }).map((_, i) => (\n <InputOTPSlot\n key={i}\n index={i}\n className=\"w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200\"\n />\n ))}\n </InputOTPGroup>\n </InputOTP>\n </div>\n </div>\n\n <div className=\"flex flex-row items-center gap-2 p-4 lg:p-5 border-t border-gray-200 mt-auto lg:mt-0\">\n <Button className=\"h-10! flex-1\" disabled={!isValid || isLoading} type=\"submit\">\n {isLoading\n ? translate('common.account.twoFactor.disabling')\n : translate('common.account.twoFactor.disable')}\n </Button>\n <Button\n variant=\"secondary\"\n className=\"h-10! flex-1\"\n type=\"button\"\n onClick={handleClose}\n >\n {translate('common.actions.cancel')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n );\n}\n"],"mappings":";AA6CgB,cAEF,YAFE;AA3ChB,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,UAAU,eAAe,oBAAoB;AACtD,SAAS,0BAA0B;AACnC,OAAO,iCAAiC;AAQzB,SAAR,0BAA2C;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,YAAY,gBAAgB;AAClC,QAAM,EAAE,MAAM,SAAS,SAAS,WAAW,aAAa,aAAa,IACnE,4BAA4B,EAAE,SAAS,UAAU,CAAC;AAEpD,SACE,oBAAC,eACC,8BAAC,UAAO,MAAY,cAAc,aAChC,8BAAC,iBAAc,WAAU,+SACvB;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,qBAAa;AAAA,MACf;AAAA,MACA,WAAU;AAAA,MAEV;AAAA,6BAAC,SAAI,WAAU,+DACb;AAAA,8BAAC,SAAI,WAAU,oEACb,8BAAC,mBAAgB,MAAM,IAAI,WAAU,iBAAgB,GACvD;AAAA,UACA,qBAAC,gBAAa,WAAU,aACtB;AAAA,gCAAC,eAAY,WAAU,2CACpB,oBAAU,uCAAuC,GACpD;AAAA,YACA,oBAAC,qBAAkB,WAAU,yCAC1B,oBAAU,6CAA6C,GAC1D;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,gCAAC,UAAK,WAAU,wCACb,oBAAU,4CAA4C,GACzD;AAAA,YACA,oBAAC,YAAS,WAAW,GAAG,SAAS,oBAAoB,OAAO,MAAM,UAAU,SAC1E,8BAAC,iBAAc,WAAU,SACtB,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO;AAAA,gBACP,WAAU;AAAA;AAAA,cAFL;AAAA,YAGP,CACD,GACH,GACF;AAAA,aACF;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,wFACb;AAAA,8BAAC,UAAO,WAAU,gBAAe,UAAU,CAAC,WAAW,WAAW,MAAK,UACpE,sBACG,UAAU,oCAAoC,IAC9C,UAAU,kCAAkC,GAClD;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,WAAU;AAAA,cACV,MAAK;AAAA,cACL,SAAS;AAAA,cAER,oBAAU,uBAAuB;AAAA;AAAA,UACpC;AAAA,WACF;AAAA;AAAA;AAAA,EACF,GACF,GACF,GACF;AAEJ;","names":[]}
@@ -32,74 +32,15 @@ function TwoFactorAuthModal({ open, onClose, onSuccess }) {
32
32
  onClose();
33
33
  };
34
34
  if (step === "recovery") {
35
- return (
36
- // Abre por cima do modal de configurações da conta, que é irmão na árvore.
37
- /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(
38
- DialogContent,
39
- {
40
- showCloseButton: false,
41
- className: "flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]",
42
- children: [
43
- /* @__PURE__ */ jsx(DialogHeader, { className: "sr-only", children: /* @__PURE__ */ jsx(DialogTitle, { children: translate("common.account.twoFactor.recoveryTitle") }) }),
44
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0", children: [
45
- /* @__PURE__ */ jsx("span", { className: "paragraph-medium-semibold text-gray-950 text-center", children: translate("common.account.twoFactor.recoveryTitle") }),
46
- /* @__PURE__ */ jsx(
47
- Button,
48
- {
49
- type: "button",
50
- variant: "ghost",
51
- className: "size-8! p-0 absolute right-4",
52
- onClick: handleClose,
53
- children: /* @__PURE__ */ jsx(IconX, { size: 18 })
54
- }
55
- )
56
- ] }),
57
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5 lg:gap-6 px-4 lg:px-5 py-5 lg:py-6 flex-1 lg:flex-none", children: [
58
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
59
- /* @__PURE__ */ jsx("h3", { className: "paragraph-medium-semibold text-gray-950 max-w-[269px]", children: translate("common.account.twoFactor.recoveryHeading") }),
60
- /* @__PURE__ */ jsxs("div", { className: "paragraph-small-regular text-gray-600", children: [
61
- /* @__PURE__ */ jsx("p", { children: translate("common.account.twoFactor.recoveryText1") }),
62
- /* @__PURE__ */ jsx("br", {}),
63
- /* @__PURE__ */ jsx("p", { children: translate("common.account.twoFactor.recoveryText2") })
64
- ] })
65
- ] }),
66
- /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg p-4 flex flex-col gap-4", children: [
67
- /* @__PURE__ */ jsx("div", { className: "paragraph-small-medium text-gray-950 whitespace-nowrap", children: backupCodes.map((backupCode, i) => /* @__PURE__ */ jsx("p", { children: backupCode }, i)) }),
68
- /* @__PURE__ */ jsxs(
69
- Button,
70
- {
71
- type: "button",
72
- variant: "secondary",
73
- className: "h-8! w-fit",
74
- onClick: handleCopyCodes,
75
- children: [
76
- /* @__PURE__ */ jsx(IconCopy, { size: 16 }),
77
- translate("common.account.twoFactor.copyCodes")
78
- ]
79
- }
80
- )
81
- ] })
82
- ] }),
83
- /* @__PURE__ */ jsxs("div", { className: "flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between mt-auto lg:mt-0", children: [
84
- /* @__PURE__ */ jsx(Button, { variant: "secondary", className: "h-10!", type: "button", onClick: handleClose, children: translate("common.actions.cancel") }),
85
- /* @__PURE__ */ jsx(Button, { className: "h-10!", type: "button", onClick: handleFinalize, children: translate("common.account.twoFactor.finish") })
86
- ] })
87
- ]
88
- }
89
- ) }) })
90
- );
91
- }
92
- return (
93
- // Abre por cima do modal de configurações da conta, que é irmão na árvore.
94
- /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(
35
+ return /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(
95
36
  DialogContent,
96
37
  {
97
38
  showCloseButton: false,
98
- className: "flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:max-h-[90vh] lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]",
39
+ className: "flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]",
99
40
  children: [
100
- /* @__PURE__ */ jsx(DialogHeader, { className: "sr-only", children: /* @__PURE__ */ jsx(DialogTitle, { children: translate("common.account.twoFactor.title") }) }),
101
- /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0", children: [
102
- /* @__PURE__ */ jsx("span", { className: "paragraph-medium-semibold text-gray-950 text-center", children: translate("common.account.twoFactor.title") }),
41
+ /* @__PURE__ */ jsx(DialogHeader, { className: "sr-only", children: /* @__PURE__ */ jsx(DialogTitle, { children: translate("common.account.twoFactor.recoveryTitle") }) }),
42
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0", children: [
43
+ /* @__PURE__ */ jsx("span", { className: "paragraph-medium-semibold text-gray-950 text-center", children: translate("common.account.twoFactor.recoveryTitle") }),
103
44
  /* @__PURE__ */ jsx(
104
45
  Button,
105
46
  {
@@ -111,75 +52,128 @@ function TwoFactorAuthModal({ open, onClose, onSuccess }) {
111
52
  }
112
53
  )
113
54
  ] }),
114
- /* @__PURE__ */ jsxs(
115
- "form",
55
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5 lg:gap-6 px-4 lg:px-5 py-5 lg:py-6 flex-1 lg:flex-none", children: [
56
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
57
+ /* @__PURE__ */ jsx("h3", { className: "paragraph-medium-semibold text-gray-950 max-w-[269px]", children: translate("common.account.twoFactor.recoveryHeading") }),
58
+ /* @__PURE__ */ jsxs("div", { className: "paragraph-small-regular text-gray-600", children: [
59
+ /* @__PURE__ */ jsx("p", { children: translate("common.account.twoFactor.recoveryText1") }),
60
+ /* @__PURE__ */ jsx("br", {}),
61
+ /* @__PURE__ */ jsx("p", { children: translate("common.account.twoFactor.recoveryText2") })
62
+ ] })
63
+ ] }),
64
+ /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg p-4 flex flex-col gap-4", children: [
65
+ /* @__PURE__ */ jsx("div", { className: "paragraph-small-medium text-gray-950 whitespace-nowrap", children: backupCodes.map((backupCode, i) => /* @__PURE__ */ jsx("p", { children: backupCode }, i)) }),
66
+ /* @__PURE__ */ jsxs(
67
+ Button,
68
+ {
69
+ type: "button",
70
+ variant: "secondary",
71
+ className: "h-8! w-fit",
72
+ onClick: handleCopyCodes,
73
+ children: [
74
+ /* @__PURE__ */ jsx(IconCopy, { size: 16 }),
75
+ translate("common.account.twoFactor.copyCodes")
76
+ ]
77
+ }
78
+ )
79
+ ] })
80
+ ] }),
81
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between mt-auto lg:mt-0", children: [
82
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", className: "h-10!", type: "button", onClick: handleClose, children: translate("common.actions.cancel") }),
83
+ /* @__PURE__ */ jsx(Button, { className: "h-10!", type: "button", onClick: handleFinalize, children: translate("common.account.twoFactor.finish") })
84
+ ] })
85
+ ]
86
+ }
87
+ ) }) });
88
+ }
89
+ return /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(
90
+ DialogContent,
91
+ {
92
+ showCloseButton: false,
93
+ className: "flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:max-h-[90vh] lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]",
94
+ children: [
95
+ /* @__PURE__ */ jsx(DialogHeader, { className: "sr-only", children: /* @__PURE__ */ jsx(DialogTitle, { children: translate("common.account.twoFactor.title") }) }),
96
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0", children: [
97
+ /* @__PURE__ */ jsx("span", { className: "paragraph-medium-semibold text-gray-950 text-center", children: translate("common.account.twoFactor.title") }),
98
+ /* @__PURE__ */ jsx(
99
+ Button,
116
100
  {
117
- className: "flex-1 min-h-0 flex flex-col",
118
- onSubmit: (e) => {
119
- e.preventDefault();
120
- if (isCodeComplete) handleSubmit();
121
- },
122
- children: [
123
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto overscroll-contain lg:overflow-visible", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4 lg:gap-8 px-4 lg:px-5 py-4 lg:py-6 pb-60 lg:pb-6", children: [
124
- /* @__PURE__ */ jsx("p", { className: "paragraph-small-regular text-gray-950 text-center max-w-full lg:max-w-[412px]", children: translate.rich("common.account.twoFactor.instructions", {
125
- link: (chunks) => /* @__PURE__ */ jsx(
126
- "a",
127
- {
128
- href: "https://support.google.com/accounts/answer/1066447",
129
- target: "_blank",
130
- rel: "noopener noreferrer",
131
- className: "paragraph-small-medium underline",
132
- children: chunks
133
- }
134
- )
135
- }) }),
136
- /* @__PURE__ */ jsx("div", { className: "border border-gray-200 rounded-lg overflow-hidden p-2 lg:p-3 flex items-center justify-center w-[136px] h-[136px] lg:w-[203px] lg:h-[203px]", children: generateFailed ? /* @__PURE__ */ jsxs(
137
- "button",
101
+ type: "button",
102
+ variant: "ghost",
103
+ className: "size-8! p-0 absolute right-4",
104
+ onClick: handleClose,
105
+ children: /* @__PURE__ */ jsx(IconX, { size: 18 })
106
+ }
107
+ )
108
+ ] }),
109
+ /* @__PURE__ */ jsxs(
110
+ "form",
111
+ {
112
+ className: "flex-1 min-h-0 flex flex-col",
113
+ onSubmit: (e) => {
114
+ e.preventDefault();
115
+ if (isCodeComplete) handleSubmit();
116
+ },
117
+ children: [
118
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto overscroll-contain lg:overflow-visible", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4 lg:gap-8 px-4 lg:px-5 py-4 lg:py-6 pb-60 lg:pb-6", children: [
119
+ /* @__PURE__ */ jsx("p", { className: "paragraph-small-regular text-gray-950 text-center max-w-full lg:max-w-[412px]", children: translate.rich("common.account.twoFactor.instructions", {
120
+ link: (chunks) => /* @__PURE__ */ jsx(
121
+ "a",
138
122
  {
139
- type: "button",
140
- onClick: () => retryGenerate(),
141
- className: "flex flex-col items-center gap-1.5 text-center",
142
- children: [
143
- /* @__PURE__ */ jsx(IconRefresh, { size: 20, className: "text-gray-400" }),
144
- /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-gray-500", children: translate("common.account.twoFactor.retry") })
145
- ]
123
+ href: "https://support.google.com/accounts/answer/1066447",
124
+ target: "_blank",
125
+ rel: "noopener noreferrer",
126
+ className: "paragraph-small-medium underline",
127
+ children: chunks
146
128
  }
147
- ) : isGenerating || !qrcode ? /* @__PURE__ */ jsx("div", { className: "skeleton w-full h-full rounded" }) : (
148
- // eslint-disable-next-line @next/next/no-img-element
149
- /* @__PURE__ */ jsx(
150
- "img",
151
- {
152
- src: qrcode,
153
- alt: translate("common.account.twoFactor.qrAlt"),
154
- className: "w-full h-full"
155
- }
156
- )
157
- ) }),
158
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 w-full", children: [
159
- /* @__PURE__ */ jsx(Separator, { className: "flex-1" }),
160
- /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-gray-500 opacity-80 whitespace-nowrap", children: translate("common.account.twoFactor.pasteDigits") }),
161
- /* @__PURE__ */ jsx(Separator, { className: "flex-1" })
162
- ] }),
163
- /* @__PURE__ */ jsx("div", { className: "scroll-mt-20", children: /* @__PURE__ */ jsx(InputOTP, { maxLength: 6, pattern: REGEXP_ONLY_DIGITS, value: code, onChange: setCode, children: /* @__PURE__ */ jsx(InputOTPGroup, { className: "gap-2", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(
164
- InputOTPSlot,
129
+ )
130
+ }) }),
131
+ /* @__PURE__ */ jsx("div", { className: "border border-gray-200 rounded-lg overflow-hidden p-2 lg:p-3 flex items-center justify-center w-[136px] h-[136px] lg:w-[203px] lg:h-[203px]", children: generateFailed ? /* @__PURE__ */ jsxs(
132
+ "button",
133
+ {
134
+ type: "button",
135
+ onClick: () => retryGenerate(),
136
+ className: "flex flex-col items-center gap-1.5 text-center",
137
+ children: [
138
+ /* @__PURE__ */ jsx(IconRefresh, { size: 20, className: "text-gray-400" }),
139
+ /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-gray-500", children: translate("common.account.twoFactor.retry") })
140
+ ]
141
+ }
142
+ ) : isGenerating || !qrcode ? /* @__PURE__ */ jsx("div", { className: "skeleton w-full h-full rounded" }) : (
143
+ // eslint-disable-next-line @next/next/no-img-element
144
+ /* @__PURE__ */ jsx(
145
+ "img",
165
146
  {
166
- index: i,
167
- className: "w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200"
168
- },
169
- i
170
- )) }) }) })
171
- ] }) }),
172
- /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between gap-2 bg-white", children: [
173
- /* @__PURE__ */ jsx(Button, { variant: "secondary", className: "h-10!", type: "button", onClick: handleClose, children: translate("common.actions.cancel") }),
174
- /* @__PURE__ */ jsx(Button, { type: "submit", className: "h-10!", disabled: !isCodeComplete || isConfirming, children: isConfirming ? translate("common.account.twoFactor.activating") : translate("common.account.twoFactor.activate") })
175
- ] })
176
- ]
177
- }
178
- )
179
- ]
180
- }
181
- ) }) })
182
- );
147
+ src: qrcode,
148
+ alt: translate("common.account.twoFactor.qrAlt"),
149
+ className: "w-full h-full"
150
+ }
151
+ )
152
+ ) }),
153
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 w-full", children: [
154
+ /* @__PURE__ */ jsx(Separator, { className: "flex-1" }),
155
+ /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-gray-500 opacity-80 whitespace-nowrap", children: translate("common.account.twoFactor.pasteDigits") }),
156
+ /* @__PURE__ */ jsx(Separator, { className: "flex-1" })
157
+ ] }),
158
+ /* @__PURE__ */ jsx("div", { className: "scroll-mt-20", children: /* @__PURE__ */ jsx(InputOTP, { maxLength: 6, pattern: REGEXP_ONLY_DIGITS, value: code, onChange: setCode, children: /* @__PURE__ */ jsx(InputOTPGroup, { className: "gap-2", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(
159
+ InputOTPSlot,
160
+ {
161
+ index: i,
162
+ className: "w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200"
163
+ },
164
+ i
165
+ )) }) }) })
166
+ ] }) }),
167
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between gap-2 bg-white", children: [
168
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", className: "h-10!", type: "button", onClick: handleClose, children: translate("common.actions.cancel") }),
169
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "h-10!", disabled: !isCodeComplete || isConfirming, children: isConfirming ? translate("common.account.twoFactor.activating") : translate("common.account.twoFactor.activate") })
170
+ ] })
171
+ ]
172
+ }
173
+ )
174
+ ]
175
+ }
176
+ ) }) });
183
177
  }
184
178
  export {
185
179
  TwoFactorAuthModal as default
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/account/TwoFactorAuthModal.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { IconX, IconCopy, IconRefresh } from '@tabler/icons-react';\nimport { Button } from '../ui/buttons/Button';\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/overlay/Dialog';\nimport { DialogLayer } from '../ui/overlay/layers';\nimport { Separator } from '../ui/data-display/Separator';\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from '../ui/form/InputOtp';\nimport { REGEXP_ONLY_DIGITS } from 'input-otp';\nimport useTwoFactorAuthForm from './hooks/useTwoFactorAuthForm';\n\ninterface TwoFactorAuthModalProps {\n open: boolean;\n onClose: () => void;\n onSuccess?: () => void;\n}\n\nexport default function TwoFactorAuthModal({ open, onClose, onSuccess }: TwoFactorAuthModalProps) {\n const translate = useTranslations();\n const {\n code,\n setCode,\n isCodeComplete,\n isGenerating,\n isConfirming,\n generateFailed,\n retryGenerate,\n qrcode,\n backupCodes,\n step,\n handleSubmit,\n handleFinalize,\n handleCopyCodes,\n resetForm,\n } = useTwoFactorAuthForm({ onSuccess, open });\n\n const handleClose = () => {\n resetForm();\n onClose();\n };\n\n if (step === 'recovery') {\n return (\n // Abre por cima do modal de configurações da conta, que é irmão na árvore.\n <DialogLayer>\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent\n showCloseButton={false}\n className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]\"\n >\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{translate('common.account.twoFactor.recoveryTitle')}</DialogTitle>\n </DialogHeader>\n <div className=\"flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0\">\n <span className=\"paragraph-medium-semibold text-gray-950 text-center\">\n {translate('common.account.twoFactor.recoveryTitle')}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"size-8! p-0 absolute right-4\"\n onClick={handleClose}\n >\n <IconX size={18} />\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-5 lg:gap-6 px-4 lg:px-5 py-5 lg:py-6 flex-1 lg:flex-none\">\n <div className=\"flex flex-col gap-2\">\n <h3 className=\"paragraph-medium-semibold text-gray-950 max-w-[269px]\">\n {translate('common.account.twoFactor.recoveryHeading')}\n </h3>\n <div className=\"paragraph-small-regular text-gray-600\">\n <p>{translate('common.account.twoFactor.recoveryText1')}</p>\n <br />\n <p>{translate('common.account.twoFactor.recoveryText2')}</p>\n </div>\n </div>\n\n <div className=\"bg-gray-50 rounded-lg p-4 flex flex-col gap-4\">\n <div className=\"paragraph-small-medium text-gray-950 whitespace-nowrap\">\n {backupCodes.map((backupCode, i) => (\n <p key={i}>{backupCode}</p>\n ))}\n </div>\n <Button\n type=\"button\"\n variant=\"secondary\"\n className=\"h-8! w-fit\"\n onClick={handleCopyCodes}\n >\n <IconCopy size={16} />\n {translate('common.account.twoFactor.copyCodes')}\n </Button>\n </div>\n </div>\n\n <div className=\"flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between mt-auto lg:mt-0\">\n <Button variant=\"secondary\" className=\"h-10!\" type=\"button\" onClick={handleClose}>\n {translate('common.actions.cancel')}\n </Button>\n <Button className=\"h-10!\" type=\"button\" onClick={handleFinalize}>\n {translate('common.account.twoFactor.finish')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n );\n }\n\n return (\n // Abre por cima do modal de configurações da conta, que é irmão na árvore.\n <DialogLayer>\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent\n showCloseButton={false}\n className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:max-h-[90vh] lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]\"\n >\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{translate('common.account.twoFactor.title')}</DialogTitle>\n </DialogHeader>\n <div className=\"shrink-0 flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0\">\n <span className=\"paragraph-medium-semibold text-gray-950 text-center\">\n {translate('common.account.twoFactor.title')}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"size-8! p-0 absolute right-4\"\n onClick={handleClose}\n >\n <IconX size={18} />\n </Button>\n </div>\n\n <form\n className=\"flex-1 min-h-0 flex flex-col\"\n onSubmit={(e) => {\n e.preventDefault();\n if (isCodeComplete) handleSubmit();\n }}\n >\n <div className=\"flex-1 min-h-0 overflow-y-auto overscroll-contain lg:overflow-visible\">\n <div className=\"flex flex-col items-center gap-4 lg:gap-8 px-4 lg:px-5 py-4 lg:py-6 pb-60 lg:pb-6\">\n <p className=\"paragraph-small-regular text-gray-950 text-center max-w-full lg:max-w-[412px]\">\n {translate.rich('common.account.twoFactor.instructions', {\n link: (chunks) => (\n <a\n href=\"https://support.google.com/accounts/answer/1066447\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"paragraph-small-medium underline\"\n >\n {chunks}\n </a>\n ),\n })}\n </p>\n\n <div className=\"border border-gray-200 rounded-lg overflow-hidden p-2 lg:p-3 flex items-center justify-center w-[136px] h-[136px] lg:w-[203px] lg:h-[203px]\">\n {generateFailed ? (\n <button\n type=\"button\"\n onClick={() => retryGenerate()}\n className=\"flex flex-col items-center gap-1.5 text-center\"\n >\n <IconRefresh size={20} className=\"text-gray-400\" />\n <span className=\"paragraph-xsmall-medium text-gray-500\">\n {translate('common.account.twoFactor.retry')}\n </span>\n </button>\n ) : isGenerating || !qrcode ? (\n <div className=\"skeleton w-full h-full rounded\" />\n ) : (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={qrcode}\n alt={translate('common.account.twoFactor.qrAlt')}\n className=\"w-full h-full\"\n />\n )}\n </div>\n\n <div className=\"flex items-center gap-3 w-full\">\n <Separator className=\"flex-1\" />\n <span className=\"paragraph-xsmall-medium text-gray-500 opacity-80 whitespace-nowrap\">\n {translate('common.account.twoFactor.pasteDigits')}\n </span>\n <Separator className=\"flex-1\" />\n </div>\n\n <div className=\"scroll-mt-20\">\n <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS} value={code} onChange={setCode}>\n <InputOTPGroup className=\"gap-2\">\n {Array.from({ length: 6 }).map((_, i) => (\n <InputOTPSlot\n key={i}\n index={i}\n className=\"w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200\"\n />\n ))}\n </InputOTPGroup>\n </InputOTP>\n </div>\n </div>\n </div>\n\n <div className=\"shrink-0 flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between gap-2 bg-white\">\n <Button variant=\"secondary\" className=\"h-10!\" type=\"button\" onClick={handleClose}>\n {translate('common.actions.cancel')}\n </Button>\n <Button type=\"submit\" className=\"h-10!\" disabled={!isCodeComplete || isConfirming}>\n {isConfirming\n ? translate('common.account.twoFactor.activating')\n : translate('common.account.twoFactor.activate')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/account/TwoFactorAuthModal.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { IconX, IconCopy, IconRefresh } from '@tabler/icons-react';\nimport { Button } from '../ui/buttons/Button';\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/overlay/Dialog';\nimport { DialogLayer } from '../ui/overlay/layers';\nimport { Separator } from '../ui/data-display/Separator';\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from '../ui/form/InputOtp';\nimport { REGEXP_ONLY_DIGITS } from 'input-otp';\nimport useTwoFactorAuthForm from './hooks/useTwoFactorAuthForm';\n\ninterface TwoFactorAuthModalProps {\n open: boolean;\n onClose: () => void;\n onSuccess?: () => void;\n}\n\nexport default function TwoFactorAuthModal({ open, onClose, onSuccess }: TwoFactorAuthModalProps) {\n const translate = useTranslations();\n const {\n code,\n setCode,\n isCodeComplete,\n isGenerating,\n isConfirming,\n generateFailed,\n retryGenerate,\n qrcode,\n backupCodes,\n step,\n handleSubmit,\n handleFinalize,\n handleCopyCodes,\n resetForm,\n } = useTwoFactorAuthForm({ onSuccess, open });\n\n const handleClose = () => {\n resetForm();\n onClose();\n };\n\n if (step === 'recovery') {\n return (\n <DialogLayer>\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent\n showCloseButton={false}\n className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]\"\n >\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{translate('common.account.twoFactor.recoveryTitle')}</DialogTitle>\n </DialogHeader>\n <div className=\"flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0\">\n <span className=\"paragraph-medium-semibold text-gray-950 text-center\">\n {translate('common.account.twoFactor.recoveryTitle')}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"size-8! p-0 absolute right-4\"\n onClick={handleClose}\n >\n <IconX size={18} />\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-5 lg:gap-6 px-4 lg:px-5 py-5 lg:py-6 flex-1 lg:flex-none\">\n <div className=\"flex flex-col gap-2\">\n <h3 className=\"paragraph-medium-semibold text-gray-950 max-w-[269px]\">\n {translate('common.account.twoFactor.recoveryHeading')}\n </h3>\n <div className=\"paragraph-small-regular text-gray-600\">\n <p>{translate('common.account.twoFactor.recoveryText1')}</p>\n <br />\n <p>{translate('common.account.twoFactor.recoveryText2')}</p>\n </div>\n </div>\n\n <div className=\"bg-gray-50 rounded-lg p-4 flex flex-col gap-4\">\n <div className=\"paragraph-small-medium text-gray-950 whitespace-nowrap\">\n {backupCodes.map((backupCode, i) => (\n <p key={i}>{backupCode}</p>\n ))}\n </div>\n <Button\n type=\"button\"\n variant=\"secondary\"\n className=\"h-8! w-fit\"\n onClick={handleCopyCodes}\n >\n <IconCopy size={16} />\n {translate('common.account.twoFactor.copyCodes')}\n </Button>\n </div>\n </div>\n\n <div className=\"flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between mt-auto lg:mt-0\">\n <Button variant=\"secondary\" className=\"h-10!\" type=\"button\" onClick={handleClose}>\n {translate('common.actions.cancel')}\n </Button>\n <Button className=\"h-10!\" type=\"button\" onClick={handleFinalize}>\n {translate('common.account.twoFactor.finish')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n );\n }\n\n return (\n <DialogLayer>\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent\n showCloseButton={false}\n className=\"flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-[100dvh] top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-[470px] lg:h-auto lg:max-h-[90vh] lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]\"\n >\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{translate('common.account.twoFactor.title')}</DialogTitle>\n </DialogHeader>\n <div className=\"shrink-0 flex items-center justify-center px-4 py-3 relative border-b border-gray-200 lg:border-b-0\">\n <span className=\"paragraph-medium-semibold text-gray-950 text-center\">\n {translate('common.account.twoFactor.title')}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n className=\"size-8! p-0 absolute right-4\"\n onClick={handleClose}\n >\n <IconX size={18} />\n </Button>\n </div>\n\n <form\n className=\"flex-1 min-h-0 flex flex-col\"\n onSubmit={(e) => {\n e.preventDefault();\n if (isCodeComplete) handleSubmit();\n }}\n >\n <div className=\"flex-1 min-h-0 overflow-y-auto overscroll-contain lg:overflow-visible\">\n <div className=\"flex flex-col items-center gap-4 lg:gap-8 px-4 lg:px-5 py-4 lg:py-6 pb-60 lg:pb-6\">\n <p className=\"paragraph-small-regular text-gray-950 text-center max-w-full lg:max-w-[412px]\">\n {translate.rich('common.account.twoFactor.instructions', {\n link: (chunks) => (\n <a\n href=\"https://support.google.com/accounts/answer/1066447\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"paragraph-small-medium underline\"\n >\n {chunks}\n </a>\n ),\n })}\n </p>\n\n <div className=\"border border-gray-200 rounded-lg overflow-hidden p-2 lg:p-3 flex items-center justify-center w-[136px] h-[136px] lg:w-[203px] lg:h-[203px]\">\n {generateFailed ? (\n <button\n type=\"button\"\n onClick={() => retryGenerate()}\n className=\"flex flex-col items-center gap-1.5 text-center\"\n >\n <IconRefresh size={20} className=\"text-gray-400\" />\n <span className=\"paragraph-xsmall-medium text-gray-500\">\n {translate('common.account.twoFactor.retry')}\n </span>\n </button>\n ) : isGenerating || !qrcode ? (\n <div className=\"skeleton w-full h-full rounded\" />\n ) : (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={qrcode}\n alt={translate('common.account.twoFactor.qrAlt')}\n className=\"w-full h-full\"\n />\n )}\n </div>\n\n <div className=\"flex items-center gap-3 w-full\">\n <Separator className=\"flex-1\" />\n <span className=\"paragraph-xsmall-medium text-gray-500 opacity-80 whitespace-nowrap\">\n {translate('common.account.twoFactor.pasteDigits')}\n </span>\n <Separator className=\"flex-1\" />\n </div>\n\n <div className=\"scroll-mt-20\">\n <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS} value={code} onChange={setCode}>\n <InputOTPGroup className=\"gap-2\">\n {Array.from({ length: 6 }).map((_, i) => (\n <InputOTPSlot\n key={i}\n index={i}\n className=\"w-11! lg:w-12! h-13! lg:h-14! rounded-lg! text-lg border-gray-200\"\n />\n ))}\n </InputOTPGroup>\n </InputOTP>\n </div>\n </div>\n </div>\n\n <div className=\"shrink-0 flex items-center p-4 lg:p-5 border-t border-gray-200 justify-between gap-2 bg-white\">\n <Button variant=\"secondary\" className=\"h-10!\" type=\"button\" onClick={handleClose}>\n {translate('common.actions.cancel')}\n </Button>\n <Button type=\"submit\" className=\"h-10!\" disabled={!isCodeComplete || isConfirming}>\n {isConfirming\n ? translate('common.account.twoFactor.activating')\n : translate('common.account.twoFactor.activate')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n );\n}\n"],"mappings":";AAmDc,cAEF,YAFE;AAjDd,SAAS,uBAAuB;AAChC,SAAS,OAAO,UAAU,mBAAmB;AAC7C,SAAS,cAAc;AACvB,SAAS,QAAQ,eAAe,cAAc,mBAAmB;AACjE,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,UAAU,eAAe,oBAAoB;AACtD,SAAS,0BAA0B;AACnC,OAAO,0BAA0B;AAQlB,SAAR,mBAAoC,EAAE,MAAM,SAAS,UAAU,GAA4B;AAChG,QAAM,YAAY,gBAAgB;AAClC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,qBAAqB,EAAE,WAAW,KAAK,CAAC;AAE5C,QAAM,cAAc,MAAM;AACxB,cAAU;AACV,YAAQ;AAAA,EACV;AAEA,MAAI,SAAS,YAAY;AACvB,WACE,oBAAC,eACC,8BAAC,UAAO,MAAY,cAAc,aAChC;AAAA,MAAC;AAAA;AAAA,QACC,iBAAiB;AAAA,QACjB,WAAU;AAAA,QAEV;AAAA,8BAAC,gBAAa,WAAU,WACtB,8BAAC,eAAa,oBAAU,wCAAwC,GAAE,GACpE;AAAA,UACA,qBAAC,SAAI,WAAU,8FACb;AAAA,gCAAC,UAAK,WAAU,uDACb,oBAAU,wCAAwC,GACrD;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACV,SAAS;AAAA,gBAET,8BAAC,SAAM,MAAM,IAAI;AAAA;AAAA,YACnB;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,8EACb;AAAA,iCAAC,SAAI,WAAU,uBACb;AAAA,kCAAC,QAAG,WAAU,yDACX,oBAAU,0CAA0C,GACvD;AAAA,cACA,qBAAC,SAAI,WAAU,yCACb;AAAA,oCAAC,OAAG,oBAAU,wCAAwC,GAAE;AAAA,gBACxD,oBAAC,QAAG;AAAA,gBACJ,oBAAC,OAAG,oBAAU,wCAAwC,GAAE;AAAA,iBAC1D;AAAA,eACF;AAAA,YAEA,qBAAC,SAAI,WAAU,iDACb;AAAA,kCAAC,SAAI,WAAU,0DACZ,sBAAY,IAAI,CAAC,YAAY,MAC5B,oBAAC,OAAW,wBAAJ,CAAe,CACxB,GACH;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,WAAU;AAAA,kBACV,SAAS;AAAA,kBAET;AAAA,wCAAC,YAAS,MAAM,IAAI;AAAA,oBACnB,UAAU,oCAAoC;AAAA;AAAA;AAAA,cACjD;AAAA,eACF;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,yFACb;AAAA,gCAAC,UAAO,SAAQ,aAAY,WAAU,SAAQ,MAAK,UAAS,SAAS,aAClE,oBAAU,uBAAuB,GACpC;AAAA,YACA,oBAAC,UAAO,WAAU,SAAQ,MAAK,UAAS,SAAS,gBAC9C,oBAAU,iCAAiC,GAC9C;AAAA,aACF;AAAA;AAAA;AAAA,IACF,GACF,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,eACC,8BAAC,UAAO,MAAY,cAAc,aAChC;AAAA,IAAC;AAAA;AAAA,MACC,iBAAiB;AAAA,MACjB,WAAU;AAAA,MAEV;AAAA,4BAAC,gBAAa,WAAU,WACtB,8BAAC,eAAa,oBAAU,gCAAgC,GAAE,GAC5D;AAAA,QACA,qBAAC,SAAI,WAAU,uGACb;AAAA,8BAAC,UAAK,WAAU,uDACb,oBAAU,gCAAgC,GAC7C;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,WAAU;AAAA,cACV,SAAS;AAAA,cAET,8BAAC,SAAM,MAAM,IAAI;AAAA;AAAA,UACnB;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,UAAU,CAAC,MAAM;AACf,gBAAE,eAAe;AACjB,kBAAI,eAAgB,cAAa;AAAA,YACnC;AAAA,YAEA;AAAA,kCAAC,SAAI,WAAU,yEACb,+BAAC,SAAI,WAAU,qFACb;AAAA,oCAAC,OAAE,WAAU,iFACV,oBAAU,KAAK,yCAAyC;AAAA,kBACvD,MAAM,CAAC,WACL;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,QAAO;AAAA,sBACP,KAAI;AAAA,sBACJ,WAAU;AAAA,sBAET;AAAA;AAAA,kBACH;AAAA,gBAEJ,CAAC,GACH;AAAA,gBAEA,oBAAC,SAAI,WAAU,+IACZ,2BACC;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM,cAAc;AAAA,oBAC7B,WAAU;AAAA,oBAEV;AAAA,0CAAC,eAAY,MAAM,IAAI,WAAU,iBAAgB;AAAA,sBACjD,oBAAC,UAAK,WAAU,yCACb,oBAAU,gCAAgC,GAC7C;AAAA;AAAA;AAAA,gBACF,IACE,gBAAgB,CAAC,SACnB,oBAAC,SAAI,WAAU,kCAAiC;AAAA;AAAA,kBAGhD;AAAA,oBAAC;AAAA;AAAA,sBACC,KAAK;AAAA,sBACL,KAAK,UAAU,gCAAgC;AAAA,sBAC/C,WAAU;AAAA;AAAA,kBACZ;AAAA,mBAEJ;AAAA,gBAEA,qBAAC,SAAI,WAAU,kCACb;AAAA,sCAAC,aAAU,WAAU,UAAS;AAAA,kBAC9B,oBAAC,UAAK,WAAU,sEACb,oBAAU,sCAAsC,GACnD;AAAA,kBACA,oBAAC,aAAU,WAAU,UAAS;AAAA,mBAChC;AAAA,gBAEA,oBAAC,SAAI,WAAU,gBACb,8BAAC,YAAS,WAAW,GAAG,SAAS,oBAAoB,OAAO,MAAM,UAAU,SAC1E,8BAAC,iBAAc,WAAU,SACtB,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC;AAAA,kBAAC;AAAA;AAAA,oBAEC,OAAO;AAAA,oBACP,WAAU;AAAA;AAAA,kBAFL;AAAA,gBAGP,CACD,GACH,GACF,GACF;AAAA,iBACF,GACF;AAAA,cAEA,qBAAC,SAAI,WAAU,iGACb;AAAA,oCAAC,UAAO,SAAQ,aAAY,WAAU,SAAQ,MAAK,UAAS,SAAS,aAClE,oBAAU,uBAAuB,GACpC;AAAA,gBACA,oBAAC,UAAO,MAAK,UAAS,WAAU,SAAQ,UAAU,CAAC,kBAAkB,cAClE,yBACG,UAAU,qCAAqC,IAC/C,UAAU,mCAAmC,GACnD;AAAA,iBACF;AAAA;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF,GACF,GACF;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/modals/ModalManager.tsx"],"sourcesContent":["'use client';\n\nimport { ComponentType, useContext } from 'react';\nimport { useModalManager } from '../../store/useModalManager';\nimport { DialogDepthContext } from '../ui/overlay/layers';\n\ninterface ModalManagerProps {\n registry: Record<string, ComponentType>;\n}\n\nexport function ModalManager({ registry }: ModalManagerProps) {\n const { modalStack } = useModalManager();\n const depth = useContext(DialogDepthContext);\n\n if (modalStack.length === 0) return null;\n\n // Modais da pilha nascem irmaos na arvore React: sem isso, todos cairiam na\n // mesma profundidade e na mesma faixa de z-index (ver layers.tsx). A\n // posicao na pilha vira a profundidade, relativa a onde o ModalManager\n // esta montado.\n return (\n <>\n {modalStack.map((modal, index) => {\n const ModalComponent = registry[modal.key];\n\n if (!ModalComponent) {\n console.warn(`Modal \"${modal.key}\" not found in registry`);\n return null;\n }\n\n return (\n <DialogDepthContext.Provider value={depth + index} key={modal.key}>\n <ModalComponent />\n </DialogDepthContext.Provider>\n );\n })}\n </>\n );\n}\n"],"mappings":";AAqBI,mBAWQ,WAXR;AAnBJ,SAAwB,kBAAkB;AAC1C,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AAM5B,SAAS,aAAa,EAAE,SAAS,GAAsB;AAC5D,QAAM,EAAE,WAAW,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,kBAAkB;AAE3C,MAAI,WAAW,WAAW,EAAG,QAAO;AAMpC,SACE,gCACG,qBAAW,IAAI,CAAC,OAAO,UAAU;AAChC,UAAM,iBAAiB,SAAS,MAAM,GAAG;AAEzC,QAAI,CAAC,gBAAgB;AACnB,cAAQ,KAAK,UAAU,MAAM,GAAG,yBAAyB;AACzD,aAAO;AAAA,IACT;AAEA,WACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,QAAQ,OAC1C,8BAAC,kBAAe,KADsC,MAAM,GAE9D;AAAA,EAEJ,CAAC,GACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/modals/ModalManager.tsx"],"sourcesContent":["'use client';\n\nimport { ComponentType, useContext } from 'react';\nimport { useModalManager } from '../../store/useModalManager';\nimport { DialogDepthContext } from '../ui/overlay/layers';\n\ninterface ModalManagerProps {\n registry: Record<string, ComponentType>;\n}\n\nexport function ModalManager({ registry }: ModalManagerProps) {\n const { modalStack } = useModalManager();\n const depth = useContext(DialogDepthContext);\n\n if (modalStack.length === 0) return null;\n\n return (\n <>\n {modalStack.map((modal, index) => {\n const ModalComponent = registry[modal.key];\n\n if (!ModalComponent) {\n console.warn(`Modal \"${modal.key}\" not found in registry`);\n return null;\n }\n\n return (\n <DialogDepthContext.Provider value={depth + index} key={modal.key}>\n <ModalComponent />\n </DialogDepthContext.Provider>\n );\n })}\n </>\n );\n}\n"],"mappings":";AAiBI,mBAWQ,WAXR;AAfJ,SAAwB,kBAAkB;AAC1C,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AAM5B,SAAS,aAAa,EAAE,SAAS,GAAsB;AAC5D,QAAM,EAAE,WAAW,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,kBAAkB;AAE3C,MAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,SACE,gCACG,qBAAW,IAAI,CAAC,OAAO,UAAU;AAChC,UAAM,iBAAiB,SAAS,MAAM,GAAG;AAEzC,QAAI,CAAC,gBAAgB;AACnB,cAAQ,KAAK,UAAU,MAAM,GAAG,yBAAyB;AACzD,aAAO;AAAA,IACT;AAEA,WACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,QAAQ,OAC1C,8BAAC,kBAAe,KADsC,MAAM,GAE9D;AAAA,EAEJ,CAAC,GACH;AAEJ;","names":[]}
@@ -21,17 +21,14 @@ function ImageTooSmallModal({
21
21
  }) {
22
22
  const translate = useTranslations();
23
23
  const dimensionText = `${minWidth}x${minHeight}px`;
24
- return (
25
- // Abre por cima do modal/uploader que a disparou.
26
- /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0", children: [
27
- /* @__PURE__ */ jsx(DialogHeader, { className: "p-5", children: /* @__PURE__ */ jsx(DialogTitle, { className: "text-center text-[16px]!", children: translate("common.imageUpload.tooSmallTitleWithDimension", { dimensionText }) }) }),
28
- /* @__PURE__ */ jsx("div", { className: "px-5 pb-5", children: /* @__PURE__ */ jsx("p", { className: "paragraph-small-regular text-zinc-600 text-center", children: translate("common.imageUpload.tooSmallBodyFileWithDimension", { fileName, dimensionText }) }) }),
29
- /* @__PURE__ */ jsxs(DialogFooter, { className: "flex-row! items-center justify-start! gap-3 p-5 border-t border-zinc-200", children: [
30
- /* @__PURE__ */ jsx(Button, { onClick: onSelectAnother, children: translate("common.imageUpload.sendAnother") }),
31
- /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onCancel, children: translate("common.actions.cancel") })
32
- ] })
33
- ] }) }) })
34
- );
24
+ return /* @__PURE__ */ jsx(DialogLayer, { children: /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0", children: [
25
+ /* @__PURE__ */ jsx(DialogHeader, { className: "p-5", children: /* @__PURE__ */ jsx(DialogTitle, { className: "text-center text-[16px]!", children: translate("common.imageUpload.tooSmallTitleWithDimension", { dimensionText }) }) }),
26
+ /* @__PURE__ */ jsx("div", { className: "px-5 pb-5", children: /* @__PURE__ */ jsx("p", { className: "paragraph-small-regular text-zinc-600 text-center", children: translate("common.imageUpload.tooSmallBodyFileWithDimension", { fileName, dimensionText }) }) }),
27
+ /* @__PURE__ */ jsxs(DialogFooter, { className: "flex-row! items-center justify-start! gap-3 p-5 border-t border-zinc-200", children: [
28
+ /* @__PURE__ */ jsx(Button, { onClick: onSelectAnother, children: translate("common.imageUpload.sendAnother") }),
29
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onCancel, children: translate("common.actions.cancel") })
30
+ ] })
31
+ ] }) }) });
35
32
  }
36
33
  export {
37
34
  ImageTooSmallModal
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/widgets/ImageUpload/ImageTooSmallModal.tsx"],"sourcesContent":["'use client'\n\nimport { useTranslations } from 'next-intl'\nimport {\n Dialog,\n DialogContent,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../../ui/overlay/Dialog'\nimport { DialogLayer } from '../../ui/overlay/layers'\nimport { Button } from '../../ui/buttons/Button'\n\ninterface ImageTooSmallModalProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n fileName: string\n minWidth: number\n minHeight: number\n onSelectAnother: () => void\n onCancel: () => void\n}\n\nexport function ImageTooSmallModal({\n open,\n onOpenChange,\n fileName,\n minWidth,\n minHeight,\n onSelectAnother,\n onCancel,\n}: ImageTooSmallModalProps) {\n const translate = useTranslations()\n const dimensionText = `${minWidth}x${minHeight}px`\n\n return (\n // Abre por cima do modal/uploader que a disparou.\n <DialogLayer>\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0\">\n <DialogHeader className=\"p-5\">\n <DialogTitle className=\"text-center text-[16px]!\">\n {translate('common.imageUpload.tooSmallTitleWithDimension', { dimensionText })}\n </DialogTitle>\n </DialogHeader>\n\n <div className=\"px-5 pb-5\">\n <p className=\"paragraph-small-regular text-zinc-600 text-center\">\n {translate('common.imageUpload.tooSmallBodyFileWithDimension', { fileName, dimensionText })}\n </p>\n </div>\n\n <DialogFooter className=\"flex-row! items-center justify-start! gap-3 p-5 border-t border-zinc-200\">\n <Button onClick={onSelectAnother}>{translate('common.imageUpload.sendAnother')}</Button>\n <Button variant=\"secondary\" onClick={onCancel}>\n {translate('common.actions.cancel')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n )\n}\n"],"mappings":";AAyCY,cAWF,YAXE;AAvCZ,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AAYhB,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,YAAY,gBAAgB;AAClC,QAAM,gBAAgB,GAAG,QAAQ,IAAI,SAAS;AAE9C;AAAA;AAAA,IAEE,oBAAC,eACC,8BAAC,UAAO,MAAY,cAClB,+BAAC,iBAAc,WAAU,+FACvB;AAAA,0BAAC,gBAAa,WAAU,OACtB,8BAAC,eAAY,WAAU,4BACpB,oBAAU,iDAAiD,EAAE,cAAc,CAAC,GAC/E,GACF;AAAA,MAEA,oBAAC,SAAI,WAAU,aACb,8BAAC,OAAE,WAAU,qDACV,oBAAU,oDAAoD,EAAE,UAAU,cAAc,CAAC,GAC5F,GACF;AAAA,MAEA,qBAAC,gBAAa,WAAU,4EACtB;AAAA,4BAAC,UAAO,SAAS,iBAAkB,oBAAU,gCAAgC,GAAE;AAAA,QAC/E,oBAAC,UAAO,SAAQ,aAAY,SAAS,UAClC,oBAAU,uBAAuB,GACpC;AAAA,SACF;AAAA,OACF,GACF,GACF;AAAA;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components/widgets/ImageUpload/ImageTooSmallModal.tsx"],"sourcesContent":["'use client'\n\nimport { useTranslations } from 'next-intl'\nimport {\n Dialog,\n DialogContent,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../../ui/overlay/Dialog'\nimport { DialogLayer } from '../../ui/overlay/layers'\nimport { Button } from '../../ui/buttons/Button'\n\ninterface ImageTooSmallModalProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n fileName: string\n minWidth: number\n minHeight: number\n onSelectAnother: () => void\n onCancel: () => void\n}\n\nexport function ImageTooSmallModal({\n open,\n onOpenChange,\n fileName,\n minWidth,\n minHeight,\n onSelectAnother,\n onCancel,\n}: ImageTooSmallModalProps) {\n const translate = useTranslations()\n const dimensionText = `${minWidth}x${minHeight}px`\n\n return (\n <DialogLayer>\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0\">\n <DialogHeader className=\"p-5\">\n <DialogTitle className=\"text-center text-[16px]!\">\n {translate('common.imageUpload.tooSmallTitleWithDimension', { dimensionText })}\n </DialogTitle>\n </DialogHeader>\n\n <div className=\"px-5 pb-5\">\n <p className=\"paragraph-small-regular text-zinc-600 text-center\">\n {translate('common.imageUpload.tooSmallBodyFileWithDimension', { fileName, dimensionText })}\n </p>\n </div>\n\n <DialogFooter className=\"flex-row! items-center justify-start! gap-3 p-5 border-t border-zinc-200\">\n <Button onClick={onSelectAnother}>{translate('common.imageUpload.sendAnother')}</Button>\n <Button variant=\"secondary\" onClick={onCancel}>\n {translate('common.actions.cancel')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </DialogLayer>\n )\n}\n"],"mappings":";AAwCY,cAWF,YAXE;AAtCZ,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AAYhB,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,YAAY,gBAAgB;AAClC,QAAM,gBAAgB,GAAG,QAAQ,IAAI,SAAS;AAE9C,SACE,oBAAC,eACC,8BAAC,UAAO,MAAY,cAClB,+BAAC,iBAAc,WAAU,+FACvB;AAAA,wBAAC,gBAAa,WAAU,OACtB,8BAAC,eAAY,WAAU,4BACpB,oBAAU,iDAAiD,EAAE,cAAc,CAAC,GAC/E,GACF;AAAA,IAEA,oBAAC,SAAI,WAAU,aACb,8BAAC,OAAE,WAAU,qDACV,oBAAU,oDAAoD,EAAE,UAAU,cAAc,CAAC,GAC5F,GACF;AAAA,IAEA,qBAAC,gBAAa,WAAU,4EACtB;AAAA,0BAAC,UAAO,SAAS,iBAAkB,oBAAU,gCAAgC,GAAE;AAAA,MAC/E,oBAAC,UAAO,SAAQ,aAAY,SAAS,UAClC,oBAAU,uBAAuB,GACpC;AAAA,OACF;AAAA,KACF,GACF,GACF;AAEJ;","names":[]}
@@ -1,5 +1,7 @@
1
+ import { headers as nextHeaders } from "next/headers";
1
2
  import { ApiError } from "./types";
2
3
  import { findWhitelabel } from "../../modules/whitelabel/actions/find-whitelabel.action";
4
+ const TRACE_HEADERS = ["sentry-trace", "baggage"];
3
5
  class ApiClient {
4
6
  baseUrl;
5
7
  apiVersion = "v1";
@@ -37,6 +39,14 @@ class ApiClient {
37
39
  if (!isFormData) {
38
40
  headers["Content-Type"] = "application/json";
39
41
  }
42
+ try {
43
+ const entrada = await nextHeaders();
44
+ for (const nome of TRACE_HEADERS) {
45
+ const valor = entrada.get(nome);
46
+ if (valor) headers[nome] = valor;
47
+ }
48
+ } catch {
49
+ }
40
50
  return headers;
41
51
  }
42
52
  async request(endpoint, config = {}) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/infra/api/client.ts"],"sourcesContent":["import { ApiError } from \"./types\";\nimport { findWhitelabel } from \"../../modules/whitelabel/actions/find-whitelabel.action\";\n\nexport interface ApiClientOptions {\n /**\n * Quando true, monta `{base}/{locale}/{wlId}{endpoint}` (ex.: r2-clone).\n * Padrão false: `{base}/v1/{locale}/{wlId}{endpoint}`.\n */\n omitApiVersion?: boolean;\n\n /** Habilita logs detalhados das requisições (útil para desenvolvimento) */\n emitLogs?: boolean;\n}\n\nexport interface RequestConfig extends RequestInit {\n timeout?: number;\n /** Padrão `json`. Use `text` quando a API devolve corpo não-JSON. */\n responseType?: \"json\" | \"text\";\n /** Força um whiteLabelId específico na URL, ignorando o findWhitelabel(). */\n whiteLabelId?: number;\n /** Força um token específico no header Authorization, ignorando o findWhitelabel(). */\n authToken?: string;\n}\n\nclass ApiClient {\n private readonly baseUrl: string;\n private readonly apiVersion = \"v1\";\n private readonly apiLocale = \"pt-br\";\n private readonly omitApiVersion: boolean;\n private readonly emitLogs: boolean = false;\n\n constructor(baseUrl: string, options?: ApiClientOptions) {\n this.baseUrl = baseUrl.replace(/\\/$/, \"\");\n this.omitApiVersion = options?.omitApiVersion ?? false;\n this.emitLogs = options?.emitLogs ?? false;\n }\n\n private async buildUrl(\n endpoint: string,\n whiteLabelId: number,\n ): Promise<string> {\n const wlPath = this.omitApiVersion\n ? `/${this.apiLocale}/${whiteLabelId}`\n : `/${this.apiVersion}/${this.apiLocale}/${whiteLabelId}`;\n const path = endpoint.startsWith(\"/\") ? endpoint : `/${endpoint}`;\n const url = `${this.baseUrl}${wlPath}${path}`;\n if (this.emitLogs) {\n console.log(\"[ApiClient] buildUrl\", {\n baseURL: this.baseUrl,\n whiteLabelId,\n endpoint,\n fullUrl: url,\n });\n }\n return url;\n }\n\n private async getDefaultHeaders(\n token: string,\n isFormData = false,\n ): Promise<HeadersInit> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] getDefaultHeaders\", {\n token: token.substring(0, 20) + \"...\",\n });\n }\n\n const headers: HeadersInit = {\n authorization: token,\n };\n\n if (!isFormData) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n return headers;\n }\n\n async request<T>(endpoint: string, config: RequestConfig = {}): Promise<T> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] request called\", {\n endpoint,\n method: config.method,\n });\n }\n\n const { timeout = 30000, responseType = \"json\", whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n if (this.emitLogs) {\n console.log(\"[ApiClient] Building URL and headers...\");\n }\n\n const { id, token } = await findWhitelabel();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? id),\n this.getDefaultHeaders(overrideToken ?? token, fetchConfig.body instanceof FormData),\n ]);\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Fetching\", {\n url,\n method: fetchConfig.method,\n headers: defaultHeaders,\n });\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: {\n ...defaultHeaders,\n ...fetchConfig.headers,\n },\n signal: controller.signal,\n });\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Response received\", {\n status: response.status,\n ok: response.ok,\n });\n\n if (!response.ok) {\n const errBody = await response.text();\n let message = `HTTP error ${response.status} ${response.statusText} ${errBody}`;\n let code = \"HTTP_ERROR\";\n try {\n const errorData = JSON.parse(errBody) as Record<string, unknown>;\n // @ts-ignore -- msg pode ser string ou objeto com .message\n message = errorData.message?.message ?? errorData.message ?? message;\n if (typeof errorData.code === \"string\") code = errorData.code;\n } catch {\n if (errBody.trim()) message = errBody.trim();\n }\n const method = fetchConfig.method ?? \"GET\";\n let bodyPreview: unknown;\n if (method !== \"GET\" && fetchConfig.body != null) {\n if (typeof fetchConfig.body === \"string\") {\n try {\n bodyPreview = JSON.parse(fetchConfig.body);\n } catch {\n bodyPreview = fetchConfig.body;\n }\n } else if (fetchConfig.body instanceof FormData) {\n bodyPreview = \"[FormData]\";\n } else {\n bodyPreview = fetchConfig.body;\n }\n }\n console.error(\"[ApiClient] Request failed\", {\n from: url,\n method,\n ...(bodyPreview !== undefined ? { body: bodyPreview } : {}),\n status: response.status,\n message,\n });\n throw new ApiError(message, code, response.status);\n }\n\n if (responseType === \"text\") {\n return (await response.text()) as T;\n }\n\n const data = (await response.json()) as T;\n if (this.emitLogs)\n console.log(\"[ApiClient] Request successful\", { hasData: !!data });\n return data;\n } catch (error) {\n if (this.emitLogs) console.error(\"[ApiClient] Request error\", { error });\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"Tempo de requisição excedido\", \"TIMEOUT\", 408);\n }\n throw new ApiError(error.message, \"NETWORK_ERROR\", 0);\n }\n\n throw new ApiError(\"Erro desconhecido\", \"UNKNOWN_ERROR\", 500);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async get<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"GET\" });\n }\n\n async post<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"POST\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async put<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PUT\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async patch<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PATCH\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async delete<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"DELETE\" });\n }\n\n /**\n * Requisição cuja resposta é consumida em stream: devolve a `Response` crua, sem ler o\n * corpo e sem timeout.\n *\n * `request()` não serve para isso por dois motivos. Ele faz `await response.json()`, o\n * que espera o corpo inteiro e mata o propósito do stream; e aborta em 30s, que é o\n * tempo que uma operação longa leva justamente por ser longa — gerar o link de uma\n * página com 132 imagens copia 100 MB e passa disso (ID-5112).\n *\n * O corpo é do chamador: quem recebe decide quando ler e quando cancelar.\n */\n async stream(endpoint: string, config: RequestConfig = {}): Promise<Response> {\n const { timeout: _timeout, responseType: _responseType, whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n // Só resolve o whitelabel quando falta algum dos dois: com ambos vindos do chamador, a\n // consulta seria uma ida à rede para descartar o resultado.\n const resolvido = overrideWlId != null && overrideToken != null\n ? { id: overrideWlId, token: overrideToken }\n : await findWhitelabel();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? resolvido.id),\n this.getDefaultHeaders(overrideToken ?? resolvido.token),\n ]);\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: { ...defaultHeaders, ...fetchConfig.headers },\n // Sem isto o fetch do Next segura o corpo inteiro antes de devolver, e o stream chega\n // completo de uma vez no fim: medido em 32s até o primeiro evento, contra 0,4s com\n // no-store. O progresso existiria no protocolo e não na tela.\n cache: \"no-store\",\n });\n\n if (!response.ok || !response.body) {\n const detalhe = await response.text().catch(() => \"\");\n throw new ApiError(\n detalhe.trim() || `HTTP error ${response.status} ${response.statusText}`,\n \"HTTP_ERROR\",\n response.status,\n );\n }\n\n return response;\n }\n}\n\nconst api = {\n apps: new ApiClient(\n process.env.GAPPS_R3_API_URL || \"https://r3-api.greatapps.dev.br\",\n ),\n pages: new ApiClient(\n process.env.GPAGES_R3_API_URL || \"https://r3-api.greatpages.dev.br\",\n ),\n};\n\n/** @deprecated use api.apps */\nexport const apiClient = api.apps;\n\nexport { api, ApiClient };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/infra/api/client.ts"],"sourcesContent":["import { headers as nextHeaders } from \"next/headers\";\n\nimport { ApiError } from \"./types\";\nimport { findWhitelabel } from \"../../modules/whitelabel/actions/find-whitelabel.action\";\n\n/**\n * Headers de tracing distribuído do Sentry, repassados da requisição que chegou neste worker\n * para a chamada de saída à r3-api.\n *\n * ⚠️ POR QUE PRECISA SER EXPLÍCITO. O browser já gera o trace e já manda `sentry-trace`/`baggage`\n * para o worker Next (same-origin, o browser SDK anexa sozinho). O que quebra é o SEGUNDO hop:\n * este `ApiClient` montava `{ authorization, Content-Type }` do zero e descartava o contexto, e\n * `@sentry/nextjs` rodando em workerd não instrumenta fetch de saída (a auto-instrumentação de\n * `undici` é do runtime Node e não tem onde se enganchar aqui). Sem estas duas linhas, um incidente\n * não se segue do browser até o backend: são dois traces sem ligação nenhuma.\n *\n * Do lado da r3-api os campos entram no log estruturado como `trace_id`/`parent_span_id`\n * (ver `docs/logs-estruturados.md` lá), então o MESMO id aparece no Sentry do front e no Workers\n * Logs do backend — uma busca resolve os dois.\n */\nconst TRACE_HEADERS = [\"sentry-trace\", \"baggage\"] as const;\n\nexport interface ApiClientOptions {\n /**\n * Quando true, monta `{base}/{locale}/{wlId}{endpoint}` (ex.: r2-clone).\n * Padrão false: `{base}/v1/{locale}/{wlId}{endpoint}`.\n */\n omitApiVersion?: boolean;\n\n /** Habilita logs detalhados das requisições (útil para desenvolvimento) */\n emitLogs?: boolean;\n}\n\nexport interface RequestConfig extends RequestInit {\n timeout?: number;\n /** Padrão `json`. Use `text` quando a API devolve corpo não-JSON. */\n responseType?: \"json\" | \"text\";\n /** Força um whiteLabelId específico na URL, ignorando o findWhitelabel(). */\n whiteLabelId?: number;\n /** Força um token específico no header Authorization, ignorando o findWhitelabel(). */\n authToken?: string;\n}\n\nclass ApiClient {\n private readonly baseUrl: string;\n private readonly apiVersion = \"v1\";\n private readonly apiLocale = \"pt-br\";\n private readonly omitApiVersion: boolean;\n private readonly emitLogs: boolean = false;\n\n constructor(baseUrl: string, options?: ApiClientOptions) {\n this.baseUrl = baseUrl.replace(/\\/$/, \"\");\n this.omitApiVersion = options?.omitApiVersion ?? false;\n this.emitLogs = options?.emitLogs ?? false;\n }\n\n private async buildUrl(\n endpoint: string,\n whiteLabelId: number,\n ): Promise<string> {\n const wlPath = this.omitApiVersion\n ? `/${this.apiLocale}/${whiteLabelId}`\n : `/${this.apiVersion}/${this.apiLocale}/${whiteLabelId}`;\n const path = endpoint.startsWith(\"/\") ? endpoint : `/${endpoint}`;\n const url = `${this.baseUrl}${wlPath}${path}`;\n if (this.emitLogs) {\n console.log(\"[ApiClient] buildUrl\", {\n baseURL: this.baseUrl,\n whiteLabelId,\n endpoint,\n fullUrl: url,\n });\n }\n return url;\n }\n\n private async getDefaultHeaders(\n token: string,\n isFormData = false,\n ): Promise<HeadersInit> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] getDefaultHeaders\", {\n token: token.substring(0, 20) + \"...\",\n });\n }\n\n const headers: HeadersInit = {\n authorization: token,\n };\n\n if (!isFormData) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n /**\n * Repassa o tracing quando existe. Sem mecanismo novo: `request()` já aguarda\n * `findWhitelabel()`, que já é um `'use server'` lendo `next/headers` — é a mesma chamada, no\n * mesmo ponto do fluxo. Fica aqui dentro de propósito: os dois call sites de\n * `getDefaultHeaders` passam a propagar sem precisar lembrar disso em cada um.\n *\n * Silencioso por design: fora de um contexto de requisição (build, script, job) `headers()`\n * lança, e uma chamada de API não pode falhar por causa de observabilidade. Sem os headers, a\n * r3-api simplesmente gera o próprio `request_id` e a linha continua correlacionável — só não\n * se liga ao front.\n */\n try {\n const entrada = await nextHeaders();\n\n for (const nome of TRACE_HEADERS) {\n const valor = entrada.get(nome);\n if (valor) headers[nome] = valor;\n }\n } catch {\n /* sem contexto de requisição — segue sem tracing */\n }\n\n return headers;\n }\n\n async request<T>(endpoint: string, config: RequestConfig = {}): Promise<T> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] request called\", {\n endpoint,\n method: config.method,\n });\n }\n\n const { timeout = 30000, responseType = \"json\", whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n if (this.emitLogs) {\n console.log(\"[ApiClient] Building URL and headers...\");\n }\n\n const { id, token } = await findWhitelabel();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? id),\n this.getDefaultHeaders(overrideToken ?? token, fetchConfig.body instanceof FormData),\n ]);\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Fetching\", {\n url,\n method: fetchConfig.method,\n headers: defaultHeaders,\n });\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: {\n ...defaultHeaders,\n ...fetchConfig.headers,\n },\n signal: controller.signal,\n });\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Response received\", {\n status: response.status,\n ok: response.ok,\n });\n\n if (!response.ok) {\n const errBody = await response.text();\n let message = `HTTP error ${response.status} ${response.statusText} ${errBody}`;\n let code = \"HTTP_ERROR\";\n try {\n const errorData = JSON.parse(errBody) as Record<string, unknown>;\n // @ts-ignore -- msg pode ser string ou objeto com .message\n message = errorData.message?.message ?? errorData.message ?? message;\n if (typeof errorData.code === \"string\") code = errorData.code;\n } catch {\n if (errBody.trim()) message = errBody.trim();\n }\n const method = fetchConfig.method ?? \"GET\";\n let bodyPreview: unknown;\n if (method !== \"GET\" && fetchConfig.body != null) {\n if (typeof fetchConfig.body === \"string\") {\n try {\n bodyPreview = JSON.parse(fetchConfig.body);\n } catch {\n bodyPreview = fetchConfig.body;\n }\n } else if (fetchConfig.body instanceof FormData) {\n bodyPreview = \"[FormData]\";\n } else {\n bodyPreview = fetchConfig.body;\n }\n }\n console.error(\"[ApiClient] Request failed\", {\n from: url,\n method,\n ...(bodyPreview !== undefined ? { body: bodyPreview } : {}),\n status: response.status,\n message,\n });\n throw new ApiError(message, code, response.status);\n }\n\n if (responseType === \"text\") {\n return (await response.text()) as T;\n }\n\n const data = (await response.json()) as T;\n if (this.emitLogs)\n console.log(\"[ApiClient] Request successful\", { hasData: !!data });\n return data;\n } catch (error) {\n if (this.emitLogs) console.error(\"[ApiClient] Request error\", { error });\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"Tempo de requisição excedido\", \"TIMEOUT\", 408);\n }\n throw new ApiError(error.message, \"NETWORK_ERROR\", 0);\n }\n\n throw new ApiError(\"Erro desconhecido\", \"UNKNOWN_ERROR\", 500);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async get<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"GET\" });\n }\n\n async post<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"POST\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async put<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PUT\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async patch<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PATCH\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async delete<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"DELETE\" });\n }\n\n /**\n * Requisição cuja resposta é consumida em stream: devolve a `Response` crua, sem ler o\n * corpo e sem timeout.\n *\n * `request()` não serve para isso por dois motivos. Ele faz `await response.json()`, o\n * que espera o corpo inteiro e mata o propósito do stream; e aborta em 30s, que é o\n * tempo que uma operação longa leva justamente por ser longa — gerar o link de uma\n * página com 132 imagens copia 100 MB e passa disso (ID-5112).\n *\n * O corpo é do chamador: quem recebe decide quando ler e quando cancelar.\n */\n async stream(endpoint: string, config: RequestConfig = {}): Promise<Response> {\n const { timeout: _timeout, responseType: _responseType, whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n // Só resolve o whitelabel quando falta algum dos dois: com ambos vindos do chamador, a\n // consulta seria uma ida à rede para descartar o resultado.\n const resolvido = overrideWlId != null && overrideToken != null\n ? { id: overrideWlId, token: overrideToken }\n : await findWhitelabel();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? resolvido.id),\n this.getDefaultHeaders(overrideToken ?? resolvido.token),\n ]);\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: { ...defaultHeaders, ...fetchConfig.headers },\n // Sem isto o fetch do Next segura o corpo inteiro antes de devolver, e o stream chega\n // completo de uma vez no fim: medido em 32s até o primeiro evento, contra 0,4s com\n // no-store. O progresso existiria no protocolo e não na tela.\n cache: \"no-store\",\n });\n\n if (!response.ok || !response.body) {\n const detalhe = await response.text().catch(() => \"\");\n throw new ApiError(\n detalhe.trim() || `HTTP error ${response.status} ${response.statusText}`,\n \"HTTP_ERROR\",\n response.status,\n );\n }\n\n return response;\n }\n}\n\nconst api = {\n apps: new ApiClient(\n process.env.GAPPS_R3_API_URL || \"https://r3-api.greatapps.dev.br\",\n ),\n pages: new ApiClient(\n process.env.GPAGES_R3_API_URL || \"https://r3-api.greatpages.dev.br\",\n ),\n};\n\n/** @deprecated use api.apps */\nexport const apiClient = api.apps;\n\nexport { api, ApiClient };\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.778",
3
+ "version": "1.1.779",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -31,7 +31,6 @@ export default function DisableTwoFactorAuthModal({
31
31
  useDisableTwoFactorAuthForm({ onClose, onSuccess });
32
32
 
33
33
  return (
34
- // Abre por cima do modal de configurações da conta, que é irmão na árvore.
35
34
  <DialogLayer>
36
35
  <Dialog open={open} onOpenChange={handleClose}>
37
36
  <DialogContent className="flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-117.5 lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]">
@@ -42,7 +42,6 @@ export default function TwoFactorAuthModal({ open, onClose, onSuccess }: TwoFact
42
42
 
43
43
  if (step === 'recovery') {
44
44
  return (
45
- // Abre por cima do modal de configurações da conta, que é irmão na árvore.
46
45
  <DialogLayer>
47
46
  <Dialog open={open} onOpenChange={handleClose}>
48
47
  <DialogContent
@@ -111,7 +110,6 @@ export default function TwoFactorAuthModal({ open, onClose, onSuccess }: TwoFact
111
110
  }
112
111
 
113
112
  return (
114
- // Abre por cima do modal de configurações da conta, que é irmão na árvore.
115
113
  <DialogLayer>
116
114
  <Dialog open={open} onOpenChange={handleClose}>
117
115
  <DialogContent
@@ -14,10 +14,6 @@ export function ModalManager({ registry }: ModalManagerProps) {
14
14
 
15
15
  if (modalStack.length === 0) return null;
16
16
 
17
- // Modais da pilha nascem irmaos na arvore React: sem isso, todos cairiam na
18
- // mesma profundidade e na mesma faixa de z-index (ver layers.tsx). A
19
- // posicao na pilha vira a profundidade, relativa a onde o ModalManager
20
- // esta montado.
21
17
  return (
22
18
  <>
23
19
  {modalStack.map((modal, index) => {
@@ -34,7 +34,6 @@ export function ImageTooSmallModal({
34
34
  const dimensionText = `${minWidth}x${minHeight}px`
35
35
 
36
36
  return (
37
- // Abre por cima do modal/uploader que a disparou.
38
37
  <DialogLayer>
39
38
  <Dialog open={open} onOpenChange={onOpenChange}>
40
39
  <DialogContent className="w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0">
@@ -1,6 +1,25 @@
1
+ import { headers as nextHeaders } from "next/headers";
2
+
1
3
  import { ApiError } from "./types";
2
4
  import { findWhitelabel } from "../../modules/whitelabel/actions/find-whitelabel.action";
3
5
 
6
+ /**
7
+ * Headers de tracing distribuído do Sentry, repassados da requisição que chegou neste worker
8
+ * para a chamada de saída à r3-api.
9
+ *
10
+ * ⚠️ POR QUE PRECISA SER EXPLÍCITO. O browser já gera o trace e já manda `sentry-trace`/`baggage`
11
+ * para o worker Next (same-origin, o browser SDK anexa sozinho). O que quebra é o SEGUNDO hop:
12
+ * este `ApiClient` montava `{ authorization, Content-Type }` do zero e descartava o contexto, e
13
+ * `@sentry/nextjs` rodando em workerd não instrumenta fetch de saída (a auto-instrumentação de
14
+ * `undici` é do runtime Node e não tem onde se enganchar aqui). Sem estas duas linhas, um incidente
15
+ * não se segue do browser até o backend: são dois traces sem ligação nenhuma.
16
+ *
17
+ * Do lado da r3-api os campos entram no log estruturado como `trace_id`/`parent_span_id`
18
+ * (ver `docs/logs-estruturados.md` lá), então o MESMO id aparece no Sentry do front e no Workers
19
+ * Logs do backend — uma busca resolve os dois.
20
+ */
21
+ const TRACE_HEADERS = ["sentry-trace", "baggage"] as const;
22
+
4
23
  export interface ApiClientOptions {
5
24
  /**
6
25
  * Quando true, monta `{base}/{locale}/{wlId}{endpoint}` (ex.: r2-clone).
@@ -73,6 +92,28 @@ class ApiClient {
73
92
  headers["Content-Type"] = "application/json";
74
93
  }
75
94
 
95
+ /**
96
+ * Repassa o tracing quando existe. Sem mecanismo novo: `request()` já aguarda
97
+ * `findWhitelabel()`, que já é um `'use server'` lendo `next/headers` — é a mesma chamada, no
98
+ * mesmo ponto do fluxo. Fica aqui dentro de propósito: os dois call sites de
99
+ * `getDefaultHeaders` passam a propagar sem precisar lembrar disso em cada um.
100
+ *
101
+ * Silencioso por design: fora de um contexto de requisição (build, script, job) `headers()`
102
+ * lança, e uma chamada de API não pode falhar por causa de observabilidade. Sem os headers, a
103
+ * r3-api simplesmente gera o próprio `request_id` e a linha continua correlacionável — só não
104
+ * se liga ao front.
105
+ */
106
+ try {
107
+ const entrada = await nextHeaders();
108
+
109
+ for (const nome of TRACE_HEADERS) {
110
+ const valor = entrada.get(nome);
111
+ if (valor) headers[nome] = valor;
112
+ }
113
+ } catch {
114
+ /* sem contexto de requisição — segue sem tracing */
115
+ }
116
+
76
117
  return headers;
77
118
  }
78
119