@greatapps/common 1.1.784 → 1.1.786

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.
Files changed (121) hide show
  1. package/dist/components/layouts/AppMobileNavBar.mjs +5 -64
  2. package/dist/components/layouts/AppMobileNavBar.mjs.map +1 -1
  3. package/dist/components/layouts/AppNavBar.mjs +0 -2
  4. package/dist/components/layouts/AppNavBar.mjs.map +1 -1
  5. package/dist/components/layouts/NavBar.mjs +52 -111
  6. package/dist/components/layouts/NavBar.mjs.map +1 -1
  7. package/dist/components/modals/cards/AddCardModal.mjs +3 -2
  8. package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
  9. package/dist/components/ui/form/PhoneInput.mjs +1 -1
  10. package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
  11. package/dist/handlers.mjs +0 -2
  12. package/dist/handlers.mjs.map +1 -1
  13. package/dist/i18n/messages/en-us.mjs +1 -3
  14. package/dist/i18n/messages/en-us.mjs.map +1 -1
  15. package/dist/i18n/messages/es-es.mjs +1 -3
  16. package/dist/i18n/messages/es-es.mjs.map +1 -1
  17. package/dist/i18n/messages/pt-br.mjs +1 -3
  18. package/dist/i18n/messages/pt-br.mjs.map +1 -1
  19. package/dist/index.mjs +3 -10
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/infra/api/client.mjs +1 -5
  22. package/dist/infra/api/client.mjs.map +1 -1
  23. package/dist/infra/api/types.mjs +1 -1
  24. package/dist/infra/api/types.mjs.map +1 -1
  25. package/dist/infra/route/safe-route-handler.mjs +1 -4
  26. package/dist/infra/route/safe-route-handler.mjs.map +1 -1
  27. package/dist/middlewares/create-auth-middleware.mjs +11 -1
  28. package/dist/middlewares/create-auth-middleware.mjs.map +1 -1
  29. package/dist/modules/auth/utils/assert-account-not-frozen.mjs +5 -6
  30. package/dist/modules/auth/utils/assert-account-not-frozen.mjs.map +1 -1
  31. package/dist/modules/auth/utils/assert-management-subscription.mjs +14 -7
  32. package/dist/modules/auth/utils/assert-management-subscription.mjs.map +1 -1
  33. package/dist/modules/auth/utils/assert-paid-subscription.mjs +12 -7
  34. package/dist/modules/auth/utils/assert-paid-subscription.mjs.map +1 -1
  35. package/dist/modules/auth/utils/assert-subscription-addon.mjs +10 -2
  36. package/dist/modules/auth/utils/assert-subscription-addon.mjs.map +1 -1
  37. package/dist/modules/plans/hooks/use-plan-by-id.hook.mjs.map +1 -1
  38. package/dist/modules/subscriptions/actions/update-subscription-payment.action.mjs.map +1 -1
  39. package/dist/modules/subscriptions/constants/subscription.constants.mjs +0 -2
  40. package/dist/modules/subscriptions/constants/subscription.constants.mjs.map +1 -1
  41. package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs +13 -2
  42. package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs.map +1 -1
  43. package/dist/modules/subscriptions/hooks/update-subscription-payment.hook.mjs +10 -2
  44. package/dist/modules/subscriptions/hooks/update-subscription-payment.hook.mjs.map +1 -1
  45. package/dist/modules/subscriptions/services/subscriptions.service.mjs +12 -27
  46. package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -1
  47. package/dist/modules/subscriptions/types/subscription.type.mjs.map +1 -1
  48. package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs +1 -1
  49. package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs.map +1 -1
  50. package/dist/modules/subscriptions/utils/has-paid-subscription.mjs +1 -1
  51. package/dist/modules/subscriptions/utils/has-paid-subscription.mjs.map +1 -1
  52. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs +9 -6
  53. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -1
  54. package/dist/modules/whitelabel/services/whitelabel.service.mjs +28 -2
  55. package/dist/modules/whitelabel/services/whitelabel.service.mjs.map +1 -1
  56. package/dist/providers/auth.provider.mjs +12 -9
  57. package/dist/providers/auth.provider.mjs.map +1 -1
  58. package/dist/server.mjs +0 -4
  59. package/dist/server.mjs.map +1 -1
  60. package/dist/store/useMdSidebarStore.mjs +4 -17
  61. package/dist/store/useMdSidebarStore.mjs.map +1 -1
  62. package/dist/utils/redirect.mjs +10 -0
  63. package/dist/utils/redirect.mjs.map +1 -1
  64. package/dist/utils/safeServerAction.mjs +1 -4
  65. package/dist/utils/safeServerAction.mjs.map +1 -1
  66. package/dist/utils/withAction.mjs +1 -2
  67. package/dist/utils/withAction.mjs.map +1 -1
  68. package/package.json +1 -1
  69. package/src/components/layouts/AppMobileNavBar.tsx +6 -90
  70. package/src/components/layouts/AppNavBar.tsx +1 -5
  71. package/src/components/layouts/NavBar.tsx +66 -160
  72. package/src/components/modals/cards/AddCardModal.tsx +15 -2
  73. package/src/components/ui/form/PhoneInput.tsx +1 -1
  74. package/src/handlers.ts +0 -1
  75. package/src/i18n/messages/en-us.ts +0 -2
  76. package/src/i18n/messages/es-es.ts +0 -2
  77. package/src/i18n/messages/pt-br.ts +0 -2
  78. package/src/index.ts +3 -8
  79. package/src/infra/api/client.ts +3 -11
  80. package/src/infra/api/types.ts +5 -7
  81. package/src/infra/route/safe-route-handler.ts +1 -4
  82. package/src/middlewares/create-auth-middleware.ts +12 -1
  83. package/src/modules/auth/utils/assert-account-not-frozen.ts +6 -7
  84. package/src/modules/auth/utils/assert-management-subscription.ts +23 -15
  85. package/src/modules/auth/utils/assert-paid-subscription.ts +15 -7
  86. package/src/modules/auth/utils/assert-subscription-addon.ts +12 -2
  87. package/src/modules/plans/hooks/use-plan-by-id.hook.ts +1 -1
  88. package/src/modules/subscriptions/actions/update-subscription-payment.action.ts +6 -1
  89. package/src/modules/subscriptions/constants/subscription.constants.ts +0 -3
  90. package/src/modules/subscriptions/hooks/find-active-subscription.hook.ts +17 -7
  91. package/src/modules/subscriptions/hooks/update-subscription-payment.hook.ts +21 -3
  92. package/src/modules/subscriptions/services/subscriptions.service.ts +46 -38
  93. package/src/modules/subscriptions/types/subscription.type.ts +27 -2
  94. package/src/modules/subscriptions/utils/has-active-paid-subscription.ts +2 -1
  95. package/src/modules/subscriptions/utils/has-paid-subscription.ts +2 -1
  96. package/src/modules/whitelabel/actions/find-whitelabel.action.ts +13 -9
  97. package/src/modules/whitelabel/services/whitelabel.service.ts +34 -2
  98. package/src/providers/auth.provider.tsx +13 -10
  99. package/src/server.ts +0 -2
  100. package/src/store/useMdSidebarStore.ts +5 -20
  101. package/src/utils/redirect.ts +10 -0
  102. package/src/utils/safeServerAction.ts +6 -4
  103. package/src/utils/withAction.ts +1 -2
  104. package/dist/components/layouts/GreatDomainIcon.mjs +0 -40
  105. package/dist/components/layouts/GreatDomainIcon.mjs.map +0 -1
  106. package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs +0 -10
  107. package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs.map +0 -1
  108. package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs +0 -17
  109. package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs.map +0 -1
  110. package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs +0 -28
  111. package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs.map +0 -1
  112. package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs +0 -16
  113. package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs.map +0 -1
  114. package/dist/modules/subscriptions/utils/read-current-subscription.mjs +0 -21
  115. package/dist/modules/subscriptions/utils/read-current-subscription.mjs.map +0 -1
  116. package/src/components/layouts/GreatDomainIcon.tsx +0 -43
  117. package/src/modules/subscriptions/actions/get-current-subscription.action.ts +0 -8
  118. package/src/modules/subscriptions/handlers/current-subscription.handler.ts +0 -16
  119. package/src/modules/subscriptions/hooks/use-current-subscription.hook.ts +0 -38
  120. package/src/modules/subscriptions/utils/group-subscriptions-by-product.ts +0 -23
  121. package/src/modules/subscriptions/utils/read-current-subscription.ts +0 -34
@@ -6,13 +6,11 @@ import Link from "next/link";
6
6
  import { useTranslations } from "next-intl";
7
7
  import { IconChevronRight } from "@tabler/icons-react";
8
8
  import { Separator } from "../ui/data-display/Separator";
9
- import { GreatDomainIcon } from "./GreatDomainIcon";
10
9
  import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/overlay/Tooltip";
11
10
  import {
12
11
  useDesktopSidebarStore,
13
12
  useDesktopSidebarHydration,
14
13
  } from "../../store/useDesktopSidebarStore";
15
- import { useMdSidebarHydration } from "../../store/useMdSidebarStore";
16
14
  import {
17
15
  useWhitelabel,
18
16
  useIsDefaultWhitelabel,
@@ -22,9 +20,7 @@ import { cn } from "../../infra/utils/clsx";
22
20
  type NavBarProps = {
23
21
  logoHref: string;
24
22
  appIconHref: string;
25
- /** Sem href o ícone do GreatDomain não é renderizado: é a flag do app. */
26
- domainIconHref?: string;
27
- activeApp?: "gapps" | "gpages" | "gdomain";
23
+ activeApp?: "gapps" | "gpages";
28
24
  showExpandButton?: boolean;
29
25
  onExpandClick?: () => void;
30
26
  children?: React.ReactNode;
@@ -34,7 +30,6 @@ type NavBarProps = {
34
30
  function NavBar({
35
31
  logoHref,
36
32
  appIconHref,
37
- domainIconHref,
38
33
  activeApp,
39
34
  showExpandButton = false,
40
35
  onExpandClick,
@@ -45,7 +40,6 @@ function NavBar({
45
40
  const { isExpanded: isDesktopExpanded, toggle: toggleDesktop } =
46
41
  useDesktopSidebarStore();
47
42
  useDesktopSidebarHydration();
48
- useMdSidebarHydration();
49
43
  const { whitelabel } = useWhitelabel();
50
44
  const isDefaultWl = useIsDefaultWhitelabel();
51
45
  const primaryColor = whitelabel?.general?.primary_color;
@@ -136,9 +130,7 @@ function NavBar({
136
130
  <IconChevronRight size={16} className="text-gray-500" />
137
131
  </button>
138
132
  </TooltipTrigger>
139
- <TooltipContent side="right">
140
- {translate("common.layout.navbar.expandMenu")}
141
- </TooltipContent>
133
+ <TooltipContent side="right">{translate('common.layout.navbar.expandMenu')}</TooltipContent>
142
134
  </Tooltip>
143
135
  )}
144
136
  {/* Desktop expand button */}
@@ -152,164 +144,78 @@ function NavBar({
152
144
  <IconChevronRight size={16} className="text-gray-500" />
153
145
  </button>
154
146
  </TooltipTrigger>
155
- <TooltipContent side="right">
156
- {translate("common.layout.navbar.expandMenu")}
157
- </TooltipContent>
147
+ <TooltipContent side="right">{translate('common.layout.navbar.expandMenu')}</TooltipContent>
158
148
  </Tooltip>
159
149
  )}
160
150
  </div>
161
151
 
162
- {/* Ícones de app — 8px entre eles (Figma 5929:2676) */}
163
- <div className="flex flex-col gap-2 items-center">
164
- {/* GreatPages icon */}
165
- <div className="relative">
166
- <Tooltip>
167
- <TooltipTrigger asChild>
168
- <Link
169
- href={appIconHref}
170
- className={cn(
171
- "size-10 rounded-lg cursor-pointer flex items-center justify-center border-2 ring-2 transition-colors duration-300",
172
- activeApp === "gpages"
173
- ? isDefaultWl
174
- ? "bg-cyan-100 border-white ring-cyan-300"
175
- : "border-white ring-transparent"
176
- : isDefaultWl
177
- ? "hover:bg-gray-100 border-transparent ring-transparent"
178
- : "border-transparent ring-transparent",
179
- )}
180
- style={
181
- !isDefaultWl
182
- ? {
183
- backgroundColor: primaryColor,
184
- boxShadow:
185
- activeApp === "gpages"
186
- ? `0 0 0 2px ${primaryColor}40`
187
- : undefined,
188
- opacity: activeApp !== "gpages" ? 0.2 : undefined,
189
- }
190
- : undefined
191
- }
192
- >
193
- {isDefaultWl ? (
194
- <Image
195
- src="/icons/icon-navbar.svg"
196
- alt="GreatPages"
197
- width={24}
198
- height={24}
199
- />
200
- ) : (
201
- <svg
202
- xmlns="http://www.w3.org/2000/svg"
203
- width="24"
204
- height="24"
205
- viewBox="0 0 24 24"
206
- fill="none"
207
- stroke="white"
208
- strokeWidth="2"
209
- strokeLinecap="round"
210
- strokeLinejoin="round"
211
- >
212
- <path d="M4 8h16" />
213
- <path d="M4 6a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" />
214
- <path d="M8 4v4" />
215
- </svg>
216
- )}
217
- </Link>
218
- </TooltipTrigger>
219
- <TooltipContent side="right">
220
- {isDefaultWl
221
- ? translate("common.layout.navbar.greatPages")
222
- : translate("common.layout.navbar.pages")}
223
- </TooltipContent>
224
- </Tooltip>
225
- <div
226
- className={cn(
227
- "absolute -left-4 top-1/2 -translate-y-1/2 z-10 w-[3px] h-5 rounded-r-xl transition-opacity duration-300",
228
- isDefaultWl ? "bg-cyan-300" : "",
229
- activeApp === "gpages" ? "opacity-100" : "opacity-0",
230
- )}
231
- style={
232
- !isDefaultWl ? { backgroundColor: primaryColor } : undefined
233
- }
234
- />
235
- </div>
236
-
237
- {/* GreatDomain icon */}
238
- {domainIconHref ? (
239
- <div className="relative">
240
- <Tooltip>
241
- <TooltipTrigger asChild>
242
- <Link
243
- href={domainIconHref}
244
- aria-label={
245
- isDefaultWl
246
- ? translate("common.layout.navbar.greatDomain")
247
- : translate("common.layout.navbar.domains")
248
- }
249
- className={cn(
250
- "size-10 rounded-[10px] cursor-pointer flex items-center justify-center border-2 ring-2 transition-colors duration-300",
251
- activeApp === "gdomain"
252
- ? isDefaultWl
253
- ? "bg-[#EDE9FE] border-white ring-[#C092FF]"
254
- : "border-white ring-transparent"
255
- : isDefaultWl
256
- ? "hover:bg-gray-100 border-transparent ring-transparent"
257
- : "border-transparent ring-transparent",
258
- )}
259
- style={
260
- !isDefaultWl
261
- ? {
262
- backgroundColor: primaryColor,
263
- boxShadow:
264
- activeApp === "gdomain"
265
- ? `0 0 0 2px ${primaryColor}40`
266
- : undefined,
267
- opacity: activeApp !== "gdomain" ? 0.2 : undefined,
268
- }
269
- : undefined
270
- }
271
- >
272
- {isDefaultWl ? (
273
- <GreatDomainIcon active={activeApp === "gdomain"} />
274
- ) : (
275
- <svg
276
- xmlns="http://www.w3.org/2000/svg"
277
- width="24"
278
- height="24"
279
- viewBox="0 0 24 24"
280
- fill="none"
281
- stroke="white"
282
- strokeWidth="2"
283
- strokeLinecap="round"
284
- strokeLinejoin="round"
285
- >
286
- <path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" />
287
- <path d="M3.6 9h16.8" />
288
- <path d="M3.6 15h16.8" />
289
- <path d="M11.5 3a17 17 0 0 0 0 18" />
290
- <path d="M12.5 3a17 17 0 0 1 0 18" />
291
- </svg>
292
- )}
293
- </Link>
294
- </TooltipTrigger>
295
- <TooltipContent side="right">
296
- {isDefaultWl
297
- ? translate("common.layout.navbar.greatDomain")
298
- : translate("common.layout.navbar.domains")}
299
- </TooltipContent>
300
- </Tooltip>
301
- <div
152
+ {/* GreatPages icon */}
153
+ <div className="relative">
154
+ <Tooltip>
155
+ <TooltipTrigger asChild>
156
+ <Link
157
+ href={appIconHref}
302
158
  className={cn(
303
- "absolute -left-4 top-1/2 -translate-y-1/2 z-10 w-[3px] h-5 rounded-r-xl transition-opacity duration-300",
304
- isDefaultWl ? "bg-[#984DFF]" : "",
305
- activeApp === "gdomain" ? "opacity-100" : "opacity-0",
159
+ "size-10 rounded-lg cursor-pointer flex items-center justify-center border-2 ring-2 transition-colors duration-300",
160
+ activeApp === "gpages"
161
+ ? isDefaultWl
162
+ ? "bg-cyan-100 border-white ring-cyan-300"
163
+ : "border-white ring-transparent"
164
+ : isDefaultWl
165
+ ? "hover:bg-gray-100 border-transparent ring-transparent"
166
+ : "border-transparent ring-transparent",
306
167
  )}
307
168
  style={
308
- !isDefaultWl ? { backgroundColor: primaryColor } : undefined
169
+ !isDefaultWl
170
+ ? {
171
+ backgroundColor: primaryColor,
172
+ boxShadow:
173
+ activeApp === "gpages"
174
+ ? `0 0 0 2px ${primaryColor}40`
175
+ : undefined,
176
+ opacity: activeApp !== "gpages" ? 0.2 : undefined,
177
+ }
178
+ : undefined
309
179
  }
310
- />
311
- </div>
312
- ) : null}
180
+ >
181
+ {isDefaultWl ? (
182
+ <Image
183
+ src="/icons/icon-navbar.svg"
184
+ alt="GreatPages"
185
+ width={24}
186
+ height={24}
187
+ />
188
+ ) : (
189
+ <svg
190
+ xmlns="http://www.w3.org/2000/svg"
191
+ width="24"
192
+ height="24"
193
+ viewBox="0 0 24 24"
194
+ fill="none"
195
+ stroke="white"
196
+ strokeWidth="2"
197
+ strokeLinecap="round"
198
+ strokeLinejoin="round"
199
+ >
200
+ <path d="M4 8h16" />
201
+ <path d="M4 6a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" />
202
+ <path d="M8 4v4" />
203
+ </svg>
204
+ )}
205
+ </Link>
206
+ </TooltipTrigger>
207
+ <TooltipContent side="right">
208
+ {isDefaultWl ? translate('common.layout.navbar.greatPages') : translate('common.layout.navbar.pages')}
209
+ </TooltipContent>
210
+ </Tooltip>
211
+ <div
212
+ className={cn(
213
+ "absolute -left-4 top-1/2 -translate-y-1/2 z-10 w-[3px] h-5 rounded-r-xl transition-opacity duration-300",
214
+ isDefaultWl ? "bg-cyan-300" : "",
215
+ activeApp === "gpages" ? "opacity-100" : "opacity-0",
216
+ )}
217
+ style={!isDefaultWl ? { backgroundColor: primaryColor } : undefined}
218
+ />
313
219
  </div>
314
220
  </div>
315
221
 
@@ -87,6 +87,15 @@ export default function AddCardModal() {
87
87
  const [isAuthenticating, setIsAuthenticating] = useState(false);
88
88
 
89
89
  const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;
90
+ /* `set_default` continua true por omissão — todo chamador que só quer cadastrar um cartão segue
91
+ * com o comportamento de sempre. Quem passa `false` é quem vai perguntar ao usuário, logo depois
92
+ * do cadastro, se a assinatura deve migrar para este cartão.
93
+ *
94
+ * Sem esse controle a pergunta seria decorativa: o backend, ao receber `set_default: true`, já
95
+ * grava o padrão do customer E reaponta as assinaturas cobráveis (`#propagateDefaultPm` em
96
+ * cards/services.js), então quando o modal abrisse a troca já teria acontecido e "Recusar" não
97
+ * teria nada para recusar. */
98
+ const setAsDefault = modalData.setAsDefault !== false;
90
99
 
91
100
  const createSetupIntentMutation = useCreateSetupIntent();
92
101
  const createCardMutation = useCreateCard();
@@ -232,7 +241,7 @@ export default function AddCardModal() {
232
241
  const cardResult = await createCardMutation.mutateAsync({
233
242
  setup_intent_id: intent.setup_intent_id,
234
243
  name: data.name,
235
- set_default: true,
244
+ set_default: setAsDefault,
236
245
  ...(isInternational
237
246
  ? {}
238
247
  : {
@@ -250,6 +259,11 @@ export default function AddCardModal() {
250
259
  country: data.country,
251
260
  });
252
261
 
262
+ /* Fecha ANTES de avisar quem chamou. `closeModal()` remove o topo da pilha do
263
+ * useModalManager, então um modal aberto de dentro do `onCardAdded` seria justamente o topo
264
+ * e sairia junto — abriria e fecharia no mesmo tick, sem o usuário ver nada. */
265
+ handleClose();
266
+
253
267
  if (cardResult.success && cardResult.data?.id != null) {
254
268
  onCardAdded?.(cardResult.data.id.toString());
255
269
  }
@@ -258,7 +272,6 @@ export default function AddCardModal() {
258
272
  (t) => <Toast variant="success" message={translate('common.cards.add.successToast')} toastId={t} />,
259
273
  { duration: 5000 }
260
274
  );
261
- handleClose();
262
275
  } catch (error) {
263
276
  /* O backend fail-closed devolve a razão exata da recusa (ex.: "CPF do pagador inválido —
264
277
  * confira os dígitos verificadores"), e o withAction relança como Error com essa mensagem.
@@ -45,7 +45,7 @@ export default function PhoneInput({
45
45
  const translate = useTranslations();
46
46
  const parsed = parsePhoneValue(value ?? '');
47
47
  const [selectedCountry, setSelectedCountry] = useState<Country>(parsed.country);
48
- const [phoneNumber, setPhoneNumber] = useState(formatNational(parsed.number, parsed.country.iso));
48
+ const [phoneNumber, setPhoneNumber] = useState(parsed.number);
49
49
  const [search, setSearch] = useState('');
50
50
  const [open, setOpen] = useState(false);
51
51
  const prevValueRef = useRef(value);
package/src/handlers.ts CHANGED
@@ -20,7 +20,6 @@ export { createListAvailableUsersHandler } from './modules/projects/handlers/lis
20
20
  export { createListAllAccountUsersHandler } from './modules/projects/handlers/list-all-account-users.handler';
21
21
 
22
22
  export { createListSubscriptionsHandler } from './modules/subscriptions/handlers/list-subscriptions.handler';
23
- export { createCurrentSubscriptionHandler } from './modules/subscriptions/handlers/current-subscription.handler';
24
23
  export { createFreezeNoticeHandler } from './modules/subscriptions/handlers/freeze-notice.handler';
25
24
 
26
25
  export { createListMessagesHandler } from './modules/users/handlers/list-messages.handler';
@@ -540,8 +540,6 @@ const messages = {
540
540
  expandMenu: 'Expand menu',
541
541
  greatPages: 'GreatPages',
542
542
  pages: 'Pages',
543
- greatDomain: 'GreatDomain',
544
- domains: 'Domains',
545
543
  },
546
544
  profile: {
547
545
  usedCredits: 'Credits used',
@@ -541,8 +541,6 @@ const messages = {
541
541
  expandMenu: 'Expandir menú',
542
542
  greatPages: 'GreatPages',
543
543
  pages: 'Páginas',
544
- greatDomain: 'GreatDomain',
545
- domains: 'Dominios',
546
544
  },
547
545
  profile: {
548
546
  usedCredits: 'Créditos utilizados',
@@ -620,8 +620,6 @@ const messages = {
620
620
  expandMenu: 'Expandir menu',
621
621
  greatPages: 'GreatPages',
622
622
  pages: 'Páginas',
623
- greatDomain: 'GreatDomain',
624
- domains: 'Domínios',
625
623
  },
626
624
  profile: {
627
625
  usedCredits: 'Créditos usados',
package/src/index.ts CHANGED
@@ -86,8 +86,6 @@ export {
86
86
  CHARGES_QUERY_KEY,
87
87
  FREEZE_NOTICE_QUERY_KEY,
88
88
  } from "./modules/subscriptions/constants/query-keys.constants";
89
- export { useCurrentSubscription } from "./modules/subscriptions/hooks/use-current-subscription.hook";
90
- /** @deprecated Use `useCurrentSubscription`. Sai na 1.1.784. */
91
89
  export { useActiveSubscription } from "./modules/subscriptions/hooks/find-active-subscription.hook";
92
90
  export { useCancelledSubscriptionGuard } from "./modules/subscriptions/hooks/use-cancelled-subscription-guard";
93
91
  export { useAccountFreeze } from "./modules/subscriptions/hooks/use-account-freeze.hook";
@@ -142,6 +140,7 @@ export type {
142
140
  Subscription,
143
141
  SubscriptionItem,
144
142
  FindSubscriptionsParams,
143
+ ChargeNowResult,
145
144
  } from "./modules/subscriptions/types/subscription.type";
146
145
  export {
147
146
  SubscriptionSchema,
@@ -223,11 +222,7 @@ export {
223
222
  confirmPaymentIfRequired,
224
223
  PaymentAuthenticationError,
225
224
  } from "./modules/subscriptions/utils/confirm-payment-if-required";
226
- export {
227
- SUBSCRIPTION_GRACE_PERIOD_DAYS,
228
- FREE_PLAN_ID,
229
- } from "./modules/subscriptions/constants/subscription.constants";
230
- export { groupSubscriptionsByProduct } from "./modules/subscriptions/utils/group-subscriptions-by-product";
225
+ export { SUBSCRIPTION_GRACE_PERIOD_DAYS } from "./modules/subscriptions/constants/subscription.constants";
231
226
  export { getSubscriptionCancellationState } from "./modules/subscriptions/utils/get-subscription-cancellation-state";
232
227
  export type { SubscriptionCancellationState } from "./modules/subscriptions/utils/get-subscription-cancellation-state";
233
228
  export {
@@ -296,7 +291,7 @@ export { formatShortDate, formatDateTime, calendarDateSchema, toCalendarDate, da
296
291
  export { buildQueryParams } from "./infra/utils/params";
297
292
  export { parseSchema, parseResult } from "./infra/utils/parser";
298
293
  export { withAction } from "./utils/withAction";
299
- export { resolveSafeRedirect, sameHostOr } from "./utils/redirect";
294
+ export { resolveSafeRedirect, sameHostOr, isSameSiteUrl } from "./utils/redirect";
300
295
  export { COUNTRIES, flagUrl } from "./utils/countries";
301
296
  export type { Country } from "./utils/countries";
302
297
 
@@ -31,11 +31,6 @@ export interface ApiClientOptions {
31
31
  emitLogs?: boolean;
32
32
  }
33
33
 
34
- interface ApiErrorResponseBody {
35
- message?: string | { message?: string };
36
- code?: string;
37
- }
38
-
39
34
  export interface RequestConfig extends RequestInit {
40
35
  timeout?: number;
41
36
  /** Padrão `json`. Use `text` quando a API devolve corpo não-JSON. */
@@ -174,12 +169,9 @@ class ApiClient {
174
169
  let message = `HTTP error ${response.status} ${response.statusText} ${errBody}`;
175
170
  let code = "HTTP_ERROR";
176
171
  try {
177
- const errorData: ApiErrorResponseBody = JSON.parse(errBody);
178
- if (typeof errorData.message === "string") {
179
- message = errorData.message;
180
- } else if (errorData.message?.message) {
181
- message = errorData.message.message;
182
- }
172
+ const errorData = JSON.parse(errBody) as Record<string, unknown>;
173
+ // @ts-ignore -- msg pode ser string ou objeto com .message
174
+ message = errorData.message?.message ?? errorData.message ?? message;
183
175
  if (typeof errorData.code === "string") code = errorData.code;
184
176
  } catch {
185
177
  if (errBody.trim()) message = errBody.trim();
@@ -1,9 +1,9 @@
1
1
  import z from 'zod';
2
2
 
3
- export const PaginationParamsSchema = z.object({
4
- page: z.number().min(1).optional().default(1),
5
- limit: z.number().optional().default(20),
6
- });
3
+ export const PaginationParamsSchema = z.object({
4
+ page: z.number().min(1).optional().default(1),
5
+ limit: z.number().optional().default(20),
6
+ });
7
7
 
8
8
  export interface PaginationParams extends z.infer<typeof PaginationParamsSchema> {}
9
9
 
@@ -51,8 +51,6 @@ export interface PaginatedSuccessResult<T> extends Omit<SuccessResult<T>, 'data'
51
51
  export interface ErrorResult {
52
52
  success: false;
53
53
  error?: string;
54
- code?: string;
55
- statusCode?: number;
56
54
  }
57
55
 
58
56
  export type ActionResult<T> = SuccessResult<T> | ErrorResult;
@@ -62,7 +60,7 @@ export class ApiError extends Error {
62
60
  public readonly code: string;
63
61
  public readonly statusCode: number;
64
62
 
65
- constructor(message: string = 'Erro desconhecido', code: string = 'UNKNOWN_ERROR', statusCode: number = 500) {
63
+ constructor(message: string, code: string = 'UNKNOWN_ERROR', statusCode: number = 500) {
66
64
  super(message);
67
65
  this.name = 'ApiError';
68
66
  this.code = code;
@@ -8,10 +8,7 @@ export async function safeRouteHandler<T>(fn: () => Promise<T>): Promise<Respons
8
8
  return Response.json({ success: true, data });
9
9
  } catch (error) {
10
10
  if (error instanceof ApiError) {
11
- return Response.json(
12
- { success: false, error: error.message, code: error.code },
13
- { status: error.statusCode },
14
- );
11
+ return Response.json({ success: false, error: error.message }, { status: error.statusCode });
15
12
  }
16
13
  console.error('[safeRouteHandler] erro inesperado', error);
17
14
  return Response.json({ success: false, error: 'Erro interno do servidor' }, { status: 500 });
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
2
2
 
3
3
  import { continueChain, MiddlewareConfig, MiddlewareFunction, MiddlewareResult, stopChain } from './types';
4
4
  import { whitelabelService } from '../modules/whitelabel/services/whitelabel.service';
5
+ import { isSameSiteUrl } from '../utils/redirect';
5
6
 
6
7
  function isRouteEquals(pathname: string, routes: string[]): boolean {
7
8
  return routes.some((route) => pathname === route || pathname.startsWith(`${route}/`));
@@ -27,8 +28,18 @@ export function createAuthMiddleware(options?: CreateAuthMiddlewareOptions): Mid
27
28
  const host = request.headers.get('host') || '';
28
29
  const whitelabel = await whitelabelService.getTokenByDomain(host).catch(() => null);
29
30
  const accountsUrl = whitelabel?.accounts_url;
31
+ const accountsUrlIsSameSite = !!accountsUrl && isSameSiteUrl(accountsUrl, host);
32
+
33
+ if (accountsUrl && !accountsUrlIsSameSite) {
34
+ console.error('[auth-middleware] accounts_url fora do site do host da requisição', {
35
+ host,
36
+ pathname,
37
+ whitelabelId: whitelabel?.id,
38
+ accountsUrl,
39
+ });
40
+ }
30
41
 
31
- const loginUrl = accountsUrl
42
+ const loginUrl = accountsUrlIsSameSite
32
43
  ? new URL('/login', accountsUrl)
33
44
  : new URL('/login', request.url);
34
45
 
@@ -1,23 +1,22 @@
1
1
  import "server-only";
2
2
 
3
3
  import { ApiError } from "../../../infra/api/types";
4
- import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
4
+ import { listSubscriptionsAction } from "../../subscriptions/actions/list-subscriptions.action";
5
5
  import { getAccountFreezeState } from "../../subscriptions/utils/get-account-freeze-state";
6
- import type { Subscription } from "../../subscriptions/types/subscription.type";
7
6
 
8
7
  export async function assertAccountNotFrozen(): Promise<void> {
9
- let subscription: Subscription | null;
8
+ const result = await listSubscriptionsAction({ active: true });
10
9
 
11
- try {
12
- subscription = await readCurrentSubscription();
13
- } catch (error) {
10
+ if (!result.success) {
14
11
  console.error(
15
12
  "[assertAccountNotFrozen] failed to read subscription context:",
16
- error,
13
+ result.error,
17
14
  );
18
15
  return;
19
16
  }
20
17
 
18
+ const subscription = result.data?.data?.[0];
19
+
21
20
  if (!subscription) return;
22
21
 
23
22
  if (getAccountFreezeState(subscription) !== "frozen") return;
@@ -1,23 +1,32 @@
1
1
  import "server-only";
2
2
 
3
+ import { cache } from "react";
4
+
3
5
  import { ApiError } from "../../../infra/api/types";
4
- import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
5
- import { getSubscriptionCancellationState } from "../../subscriptions/utils/get-subscription-cancellation-state";
6
+ import { listSubscriptionsAction } from "../../subscriptions/actions/list-subscriptions.action";
6
7
  import { getSubscriptionExpiryDate } from "../../subscriptions/utils/get-subscription-expiry-date";
7
8
 
8
9
  /**
9
- * A leitura por trás (`readCurrentSubscription`) já é deduplicada por request via
10
- * React.cache, então não precisa de outra camada aqui: em produção (26/08) o mesmo
11
- * carregamento disparava até quatro cópias da mesma pergunta em paralelo, com 727 ms,
12
- * 4,2 s, 5,0 s e 7,0 s, contra um p50 de 528 ms; agora é uma leitura só,
13
- * compartilhada pelos quatro gates.
10
+ * Deduplicado por request via React.cache, como `getCurrentUserProfile` e `requireValidSession`.
14
11
  *
15
- * A checagem de cancelamento é obrigatória além da de vencimento: a corrente pode
16
- * vir com `active: false` (baixa por falha de pagamento) com `date_due` ainda no
17
- * futuro, caso que só `getSubscriptionExpiryDate` não cobre.
12
+ * A leitura por trás é `/accounts/{id}/subscriptions?limit=1`, e ela é MAIS cara que o gate de
13
+ * perfil: para conta Stripe a listagem consulta a API do gateway ao vivo. Em produção (26/08) o
14
+ * mesmo carregamento disparou quatro dessas em paralelo — 727 ms, 4,2 s, 5,0 s e 7,0 s, contra um
15
+ * p50 de 528 ms para o endpoint. Elas se atrasam entre si: são cópias da mesma pergunta disputando
16
+ * a mesma fila.
18
17
  */
19
- export async function assertManagementSubscription(): Promise<void> {
20
- const subscription = await readCurrentSubscription();
18
+ export const assertManagementSubscription = cache(async () => {
19
+ const result = await listSubscriptionsAction({ active: true });
20
+
21
+ if (!result.success) {
22
+ throw new ApiError(
23
+ result.error || "Não foi possível validar a assinatura atual",
24
+ "SUBSCRIPTION_CONTEXT_FAILED",
25
+ 403,
26
+ );
27
+ }
28
+
29
+ const subscription = result.data?.data?.[0];
21
30
 
22
31
  if (!subscription) {
23
32
  throw new ApiError(
@@ -27,15 +36,14 @@ export async function assertManagementSubscription(): Promise<void> {
27
36
  );
28
37
  }
29
38
 
30
- const isCancelled = getSubscriptionCancellationState(subscription) === "cancelled";
31
39
  const expiryDate = getSubscriptionExpiryDate(subscription);
32
40
  const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();
33
41
 
34
- if (isCancelled || isExpired) {
42
+ if (isExpired) {
35
43
  throw new ApiError(
36
44
  "Sua assinatura está vencida. Assine um novo plano para continuar.",
37
45
  "SUBSCRIPTION_EXPIRED",
38
46
  403,
39
47
  );
40
48
  }
41
- }
49
+ });
@@ -1,13 +1,21 @@
1
1
  import "server-only";
2
2
 
3
3
  import { ApiError } from "../../../infra/api/types";
4
- import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
5
- import { getSubscriptionCancellationState } from "../../subscriptions/utils/get-subscription-cancellation-state";
4
+ import { listSubscriptionsAction } from "../../subscriptions/actions/list-subscriptions.action";
6
5
  import { getSubscriptionExpiryDate } from "../../subscriptions/utils/get-subscription-expiry-date";
7
- import { FREE_PLAN_ID } from "../../subscriptions/constants/subscription.constants";
8
6
 
9
7
  export async function assertPaidSubscription() {
10
- const subscription = await readCurrentSubscription();
8
+ const result = await listSubscriptionsAction({ active: true });
9
+
10
+ if (!result.success) {
11
+ throw new ApiError(
12
+ result.error || "Não foi possível validar a assinatura atual",
13
+ "SUBSCRIPTION_CONTEXT_FAILED",
14
+ 403,
15
+ );
16
+ }
17
+
18
+ const subscription = result.data?.data?.[0];
11
19
 
12
20
  if (!subscription) {
13
21
  throw new ApiError(
@@ -17,11 +25,10 @@ export async function assertPaidSubscription() {
17
25
  );
18
26
  }
19
27
 
20
- const isCancelled = getSubscriptionCancellationState(subscription) === "cancelled";
21
28
  const expiryDate = getSubscriptionExpiryDate(subscription);
22
29
  const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();
23
30
 
24
- if (isCancelled || isExpired) {
31
+ if (isExpired) {
25
32
  throw new ApiError(
26
33
  "Sua assinatura está vencida. Assine um novo plano para continuar.",
27
34
  "SUBSCRIPTION_EXPIRED",
@@ -29,7 +36,8 @@ export async function assertPaidSubscription() {
29
36
  );
30
37
  }
31
38
 
32
- if (subscription.type === 'free' || subscription.id_plan === FREE_PLAN_ID) {
39
+
40
+ if (subscription.type === 'free' || subscription.id_plan === 5) {
33
41
  throw new ApiError(
34
42
  "Este recurso está disponível apenas para planos pagos. Faça upgrade do seu plano para continuar.",
35
43
  "PAID_SUBSCRIPTION_REQUIRED",