@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.
- package/dist/components/layouts/AppMobileNavBar.mjs +5 -64
- package/dist/components/layouts/AppMobileNavBar.mjs.map +1 -1
- package/dist/components/layouts/AppNavBar.mjs +0 -2
- package/dist/components/layouts/AppNavBar.mjs.map +1 -1
- package/dist/components/layouts/NavBar.mjs +52 -111
- package/dist/components/layouts/NavBar.mjs.map +1 -1
- package/dist/components/modals/cards/AddCardModal.mjs +3 -2
- package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
- package/dist/components/ui/form/PhoneInput.mjs +1 -1
- package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
- package/dist/handlers.mjs +0 -2
- package/dist/handlers.mjs.map +1 -1
- package/dist/i18n/messages/en-us.mjs +1 -3
- package/dist/i18n/messages/en-us.mjs.map +1 -1
- package/dist/i18n/messages/es-es.mjs +1 -3
- package/dist/i18n/messages/es-es.mjs.map +1 -1
- package/dist/i18n/messages/pt-br.mjs +1 -3
- package/dist/i18n/messages/pt-br.mjs.map +1 -1
- package/dist/index.mjs +3 -10
- package/dist/index.mjs.map +1 -1
- package/dist/infra/api/client.mjs +1 -5
- package/dist/infra/api/client.mjs.map +1 -1
- package/dist/infra/api/types.mjs +1 -1
- package/dist/infra/api/types.mjs.map +1 -1
- package/dist/infra/route/safe-route-handler.mjs +1 -4
- package/dist/infra/route/safe-route-handler.mjs.map +1 -1
- package/dist/middlewares/create-auth-middleware.mjs +11 -1
- package/dist/middlewares/create-auth-middleware.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-account-not-frozen.mjs +5 -6
- package/dist/modules/auth/utils/assert-account-not-frozen.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-management-subscription.mjs +14 -7
- package/dist/modules/auth/utils/assert-management-subscription.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-paid-subscription.mjs +12 -7
- package/dist/modules/auth/utils/assert-paid-subscription.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-subscription-addon.mjs +10 -2
- package/dist/modules/auth/utils/assert-subscription-addon.mjs.map +1 -1
- package/dist/modules/plans/hooks/use-plan-by-id.hook.mjs.map +1 -1
- package/dist/modules/subscriptions/actions/update-subscription-payment.action.mjs.map +1 -1
- package/dist/modules/subscriptions/constants/subscription.constants.mjs +0 -2
- package/dist/modules/subscriptions/constants/subscription.constants.mjs.map +1 -1
- package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs +13 -2
- package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs.map +1 -1
- package/dist/modules/subscriptions/hooks/update-subscription-payment.hook.mjs +10 -2
- package/dist/modules/subscriptions/hooks/update-subscription-payment.hook.mjs.map +1 -1
- package/dist/modules/subscriptions/services/subscriptions.service.mjs +12 -27
- package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -1
- package/dist/modules/subscriptions/types/subscription.type.mjs.map +1 -1
- package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs +1 -1
- package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs.map +1 -1
- package/dist/modules/subscriptions/utils/has-paid-subscription.mjs +1 -1
- package/dist/modules/subscriptions/utils/has-paid-subscription.mjs.map +1 -1
- package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs +9 -6
- package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -1
- package/dist/modules/whitelabel/services/whitelabel.service.mjs +28 -2
- package/dist/modules/whitelabel/services/whitelabel.service.mjs.map +1 -1
- package/dist/providers/auth.provider.mjs +12 -9
- package/dist/providers/auth.provider.mjs.map +1 -1
- package/dist/server.mjs +0 -4
- package/dist/server.mjs.map +1 -1
- package/dist/store/useMdSidebarStore.mjs +4 -17
- package/dist/store/useMdSidebarStore.mjs.map +1 -1
- package/dist/utils/redirect.mjs +10 -0
- package/dist/utils/redirect.mjs.map +1 -1
- package/dist/utils/safeServerAction.mjs +1 -4
- package/dist/utils/safeServerAction.mjs.map +1 -1
- package/dist/utils/withAction.mjs +1 -2
- package/dist/utils/withAction.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/layouts/AppMobileNavBar.tsx +6 -90
- package/src/components/layouts/AppNavBar.tsx +1 -5
- package/src/components/layouts/NavBar.tsx +66 -160
- package/src/components/modals/cards/AddCardModal.tsx +15 -2
- package/src/components/ui/form/PhoneInput.tsx +1 -1
- package/src/handlers.ts +0 -1
- package/src/i18n/messages/en-us.ts +0 -2
- package/src/i18n/messages/es-es.ts +0 -2
- package/src/i18n/messages/pt-br.ts +0 -2
- package/src/index.ts +3 -8
- package/src/infra/api/client.ts +3 -11
- package/src/infra/api/types.ts +5 -7
- package/src/infra/route/safe-route-handler.ts +1 -4
- package/src/middlewares/create-auth-middleware.ts +12 -1
- package/src/modules/auth/utils/assert-account-not-frozen.ts +6 -7
- package/src/modules/auth/utils/assert-management-subscription.ts +23 -15
- package/src/modules/auth/utils/assert-paid-subscription.ts +15 -7
- package/src/modules/auth/utils/assert-subscription-addon.ts +12 -2
- package/src/modules/plans/hooks/use-plan-by-id.hook.ts +1 -1
- package/src/modules/subscriptions/actions/update-subscription-payment.action.ts +6 -1
- package/src/modules/subscriptions/constants/subscription.constants.ts +0 -3
- package/src/modules/subscriptions/hooks/find-active-subscription.hook.ts +17 -7
- package/src/modules/subscriptions/hooks/update-subscription-payment.hook.ts +21 -3
- package/src/modules/subscriptions/services/subscriptions.service.ts +46 -38
- package/src/modules/subscriptions/types/subscription.type.ts +27 -2
- package/src/modules/subscriptions/utils/has-active-paid-subscription.ts +2 -1
- package/src/modules/subscriptions/utils/has-paid-subscription.ts +2 -1
- package/src/modules/whitelabel/actions/find-whitelabel.action.ts +13 -9
- package/src/modules/whitelabel/services/whitelabel.service.ts +34 -2
- package/src/providers/auth.provider.tsx +13 -10
- package/src/server.ts +0 -2
- package/src/store/useMdSidebarStore.ts +5 -20
- package/src/utils/redirect.ts +10 -0
- package/src/utils/safeServerAction.ts +6 -4
- package/src/utils/withAction.ts +1 -2
- package/dist/components/layouts/GreatDomainIcon.mjs +0 -40
- package/dist/components/layouts/GreatDomainIcon.mjs.map +0 -1
- package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs +0 -10
- package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs.map +0 -1
- package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs +0 -17
- package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs.map +0 -1
- package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs +0 -28
- package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs.map +0 -1
- package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs +0 -16
- package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs.map +0 -1
- package/dist/modules/subscriptions/utils/read-current-subscription.mjs +0 -21
- package/dist/modules/subscriptions/utils/read-current-subscription.mjs.map +0 -1
- package/src/components/layouts/GreatDomainIcon.tsx +0 -43
- package/src/modules/subscriptions/actions/get-current-subscription.action.ts +0 -8
- package/src/modules/subscriptions/handlers/current-subscription.handler.ts +0 -16
- package/src/modules/subscriptions/hooks/use-current-subscription.hook.ts +0 -38
- package/src/modules/subscriptions/utils/group-subscriptions-by-product.ts +0 -23
- 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
|
-
|
|
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
|
-
{/*
|
|
163
|
-
<div className="
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
"
|
|
304
|
-
|
|
305
|
-
|
|
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
|
|
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
|
-
|
|
312
|
-
|
|
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:
|
|
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(
|
|
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';
|
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
|
|
package/src/infra/api/client.ts
CHANGED
|
@@ -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
|
|
178
|
-
|
|
179
|
-
|
|
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();
|
package/src/infra/api/types.ts
CHANGED
|
@@ -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
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
8
|
+
const result = await listSubscriptionsAction({ active: true });
|
|
10
9
|
|
|
11
|
-
|
|
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 {
|
|
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
|
-
*
|
|
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
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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
|
|
20
|
-
const
|
|
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 (
|
|
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 {
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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",
|