@greatapps/common 1.1.784 → 1.1.785
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 +1 -9
- 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/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/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/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 +2 -7
- 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/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/server.ts +0 -2
- package/src/store/useMdSidebarStore.ts +5 -20
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/find-active-subscription.hook.ts"],"sourcesContent":["\"use client\";\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/find-active-subscription.hook.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { apiFetch } from \"../../../infra/http/api-fetch\";\nimport type { PaginatedSuccessResult } from \"../../../infra/api/types\";\nimport type { Subscription } from \"../types/subscription.type\";\nimport { SUBSCRIPTIONS_QUERY_KEY } from \"../constants/query-keys.constants\";\nimport { useAuthQueryKey } from \"../../../hooks/useAuthQueryKey\";\n\nexport function useActiveSubscription() {\n const queryKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY, \"active\"]);\n\n return useQuery({\n queryKey,\n queryFn: ({ signal }) =>\n apiFetch<PaginatedSuccessResult<Subscription>>('/api/subscriptions', {\n params: { limit: 1 },\n signal,\n }),\n staleTime: 10_000,\n });\n}\n"],"mappings":";AAEA,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AAGzB,SAAS,+BAA+B;AACxC,SAAS,uBAAuB;AAEzB,SAAS,wBAAwB;AACtC,QAAM,WAAW,gBAAgB,CAAC,GAAG,yBAAyB,QAAQ,CAAC;AAEvE,SAAO,SAAS;AAAA,IACd;AAAA,IACA,SAAS,CAAC,EAAE,OAAO,MACjB,SAA+C,sBAAsB;AAAA,MACnE,QAAQ,EAAE,OAAO,EAAE;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,IACH,WAAW;AAAA,EACb,CAAC;AACH;","names":[]}
|
|
@@ -2,18 +2,26 @@
|
|
|
2
2
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
3
3
|
import { withAction } from "../../../utils/withAction";
|
|
4
4
|
import { updateSubscriptionPaymentAction } from "../actions/update-subscription-payment.action";
|
|
5
|
-
import { SUBSCRIPTIONS_QUERY_KEY } from "../constants/query-keys.constants";
|
|
5
|
+
import { SUBSCRIPTIONS_QUERY_KEY, CHARGES_QUERY_KEY } from "../constants/query-keys.constants";
|
|
6
6
|
import { useAuthQueryKey } from "../../../hooks/useAuthQueryKey";
|
|
7
|
+
import { CARDS_QUERY_KEY } from "../../cards/hooks/cards.hook";
|
|
7
8
|
function useUpdateSubscriptionPayment() {
|
|
8
9
|
const queryClient = useQueryClient();
|
|
9
10
|
const subscriptionsKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY]);
|
|
11
|
+
const cardsKey = useAuthQueryKey([...CARDS_QUERY_KEY]);
|
|
12
|
+
const chargesKey = useAuthQueryKey([...CHARGES_QUERY_KEY]);
|
|
10
13
|
return useMutation({
|
|
11
14
|
mutationFn: ({
|
|
12
15
|
subscriptionId,
|
|
13
16
|
data
|
|
14
17
|
}) => withAction(updateSubscriptionPaymentAction)(subscriptionId, data),
|
|
15
|
-
onSuccess
|
|
18
|
+
/* `onSettled`, e não `onSuccess`, pelo mesmo motivo do irmão `updateSubscriptionPlan`: a troca
|
|
19
|
+
* é aplicada no backend ANTES do 3DS, então a falha de autenticação também precisa refletir na
|
|
20
|
+
* tela. */
|
|
21
|
+
onSettled: () => {
|
|
16
22
|
queryClient.invalidateQueries({ queryKey: subscriptionsKey });
|
|
23
|
+
queryClient.invalidateQueries({ queryKey: cardsKey });
|
|
24
|
+
queryClient.invalidateQueries({ queryKey: chargesKey });
|
|
17
25
|
}
|
|
18
26
|
});
|
|
19
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/update-subscription-payment.hook.ts"],"sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { withAction } from '../../../utils/withAction';\nimport { updateSubscriptionPaymentAction } from '../actions/update-subscription-payment.action';\nimport { SUBSCRIPTIONS_QUERY_KEY } from '../constants/query-keys.constants';\nimport { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';\n\nexport function useUpdateSubscriptionPayment() {\n const queryClient = useQueryClient();\n const subscriptionsKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY]);\n\n return useMutation({\n mutationFn: ({\n subscriptionId,\n data,\n }: {\n subscriptionId: number | string;\n data: {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/update-subscription-payment.hook.ts"],"sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { withAction } from '../../../utils/withAction';\nimport { updateSubscriptionPaymentAction } from '../actions/update-subscription-payment.action';\nimport { SUBSCRIPTIONS_QUERY_KEY, CHARGES_QUERY_KEY } from '../constants/query-keys.constants';\nimport { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';\nimport { CARDS_QUERY_KEY } from '../../cards/hooks/cards.hook';\n\nexport function useUpdateSubscriptionPayment() {\n const queryClient = useQueryClient();\n const subscriptionsKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY]);\n const cardsKey = useAuthQueryKey([...CARDS_QUERY_KEY]);\n const chargesKey = useAuthQueryKey([...CHARGES_QUERY_KEY]);\n\n return useMutation({\n mutationFn: ({\n subscriptionId,\n data,\n }: {\n subscriptionId: number | string;\n data: {\n id_card?: string;\n payment_method_id?: string;\n payment_method: number;\n charge_now?: boolean;\n };\n }) => withAction(updateSubscriptionPaymentAction)(subscriptionId, data),\n /* `onSettled`, e não `onSuccess`, pelo mesmo motivo do irmão `updateSubscriptionPlan`: a troca\n * é aplicada no backend ANTES do 3DS, então a falha de autenticação também precisa refletir na\n * tela. */\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: subscriptionsKey });\n /* A troca reaponta o cartão padrão da conta na mesma operação, então sem esta invalidação o\n * selo \"padrão\" continuaria no cartão antigo depois de uma troca que já aconteceu. */\n queryClient.invalidateQueries({ queryKey: cardsKey });\n /* A operação passou a mutar um TERCEIRO estado: com `charge_now`, ela paga a fatura. A mesma\n * tela lê isso por `useCharges`, e com `refetchOnWindowFocus: false` nada refaz a busca —\n * a fatura recém-paga seguiria com selo \"pendente\" e botão \"Pagar\". */\n queryClient.invalidateQueries({ queryKey: chargesKey });\n },\n });\n}\n"],"mappings":";AAEA,SAAS,aAAa,sBAAsB;AAC5C,SAAS,kBAAkB;AAC3B,SAAS,uCAAuC;AAChD,SAAS,yBAAyB,yBAAyB;AAC3D,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAEzB,SAAS,+BAA+B;AAC7C,QAAM,cAAc,eAAe;AACnC,QAAM,mBAAmB,gBAAgB,CAAC,GAAG,uBAAuB,CAAC;AACrE,QAAM,WAAW,gBAAgB,CAAC,GAAG,eAAe,CAAC;AACrD,QAAM,aAAa,gBAAgB,CAAC,GAAG,iBAAiB,CAAC;AAEzD,SAAO,YAAY;AAAA,IACjB,YAAY,CAAC;AAAA,MACX;AAAA,MACA;AAAA,IACF,MAQM,WAAW,+BAA+B,EAAE,gBAAgB,IAAI;AAAA;AAAA;AAAA;AAAA,IAItE,WAAW,MAAM;AACf,kBAAY,kBAAkB,EAAE,UAAU,iBAAiB,CAAC;AAG5D,kBAAY,kBAAkB,EAAE,UAAU,SAAS,CAAC;AAIpD,kBAAY,kBAAkB,EAAE,UAAU,WAAW,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -38,32 +38,6 @@ class SubscriptionsService {
|
|
|
38
38
|
success: true
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
/**
|
|
42
|
-
* A assinatura corrente do plano da conta, pela regra única do backend
|
|
43
|
-
* (`GET .../subscriptions/action/current`): contrato de domínio nunca é
|
|
44
|
-
* corrente, encerrada é decidida por `active`. Sem `id_product`, paridade
|
|
45
|
-
* com hoje (melhor rank entre todos os produtos, nunca item/domínio).
|
|
46
|
-
*/
|
|
47
|
-
async getCurrentSubscription(params) {
|
|
48
|
-
const { id_account } = await getUserContext();
|
|
49
|
-
const query = buildQueryParams(
|
|
50
|
-
params
|
|
51
|
-
);
|
|
52
|
-
const url = `/accounts/${id_account}/subscriptions/action/current${query ? `?${query}` : ""}`;
|
|
53
|
-
const response = await api.apps.get(url);
|
|
54
|
-
if (response.status === 0) {
|
|
55
|
-
throw new ApiError(
|
|
56
|
-
response.message || "Erro ao consultar assinatura corrente",
|
|
57
|
-
"GET_CURRENT_SUBSCRIPTION_FAILED",
|
|
58
|
-
400
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
const raw = response.data[0];
|
|
62
|
-
return {
|
|
63
|
-
success: true,
|
|
64
|
-
data: raw ? SubscriptionSchema.parse(raw) : null
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
41
|
async calculateSubscription(data) {
|
|
68
42
|
const { id_account } = await getUserContext();
|
|
69
43
|
const response = await api.apps.post(`/accounts/${id_account}/subscriptions/action/calculate`, data);
|
|
@@ -125,7 +99,18 @@ class SubscriptionsService {
|
|
|
125
99
|
400
|
|
126
100
|
);
|
|
127
101
|
}
|
|
128
|
-
|
|
102
|
+
if (response.status === 2) {
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
applied: false,
|
|
106
|
+
...response.client_secret ? { client_secret: response.client_secret } : {}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
applied: true,
|
|
112
|
+
...response.charge_now ? { charge_now: response.charge_now } : {}
|
|
113
|
+
};
|
|
129
114
|
}
|
|
130
115
|
async getFreezeNotice() {
|
|
131
116
|
const { id_account } = await getUserContext();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/services/subscriptions.service.ts"],"sourcesContent":["import \"server-only\";\r\n\r\nimport { api } from \"../../../infra/api/client\";\r\nimport {\r\n ApiError,\r\n ApiActionResult,\r\n ApiPaginatedActionResult,\r\n PaginatedSuccessResult,\r\n SuccessResult,\r\n} from \"../../../infra/api/types\";\r\nimport { buildQueryParams } from \"../../../infra/utils/params\";\r\nimport { assertManagementPermission } from \"../../auth/utils/assert-management-permission\";\r\nimport { getUserContext } from \"../../auth/utils/get-user-context\";\r\nimport {\r\n Subscription,\r\n SubscriptionSchema,\r\n FindSubscriptionsParams,\r\n} from \"../types/subscription.type\";\r\nimport {\r\n type CalculateSubscriptionRequest,\r\n type CalculateSubscriptionResponse,\r\n type UpdateSubscriptionPlanRequest,\r\n CalculateSubscriptionResponseSchema,\r\n} from \"../types/calculate-subscription.type\";\r\nimport {\r\n SubscriptionPendingPixResponseSchema,\r\n type SubscriptionPendingPixResponse,\r\n} from \"../types/pix-pending.type\";\r\nimport type { FreezeNotice } from \"../types/freeze-notice.type\";\r\n\r\n/**\r\n * Timeout das mutations de assinatura que ESCREVEM (action/plan e action/payment).\r\n *\r\n * O default do ApiClient é 30s. Essas duas rotas caem no mesmo motor pesado do\r\n * backend (planEdit delega pra paymentEdit) — troca de método de pagamento, mandato\r\n * PIX recorrente Woovi, void-and-replace de fatura — e passam de 30s com alguma\r\n * frequência: em produção elas aparecem como `outcome=canceled` com wallTime ~29.9s.\r\n *\r\n * O problema não é o usuário ver \"Tempo excedido\": é que o abort MATA o worker no\r\n * meio da saga. Se o corte cai depois de criar o mandato Woovi e suprimir a fatura\r\n * de ciclo, sobra mandato órfão + fatura sem dunning, o usuário não recebe QR nenhum\r\n * e a próxima tentativa cria um segundo mandato. Deixar a request terminar converte\r\n * uma escrita parcial numa escrita completa.\r\n *\r\n * Não é a solução definitiva — a rota deveria ser assíncrona (202 + polling no\r\n * correlation_id). É o teto que dá folga enquanto isso.\r\n *\r\n * Só nas mutations: calculateSubscription é idempotente (não escreve nada), então\r\n * um timeout nela não deixa rastro e não precisa esperar mais.\r\n */\r\nconst SUBSCRIPTION_WRITE_TIMEOUT_MS = 60000;\r\n\r\nexport type UpdateSubscriptionPlanResponse =\r\n | Subscription\r\n | SubscriptionPendingPixResponse;\r\n\r\nclass SubscriptionsService {\r\n async listSubscriptions(\r\n params?: Partial<FindSubscriptionsParams>,\r\n ): Promise<PaginatedSuccessResult<Subscription>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const query = buildQueryParams(\r\n params as Record<string, string | boolean | undefined>,\r\n );\r\n const url = `/accounts/${id_account}/subscriptions${query ? `?${query}` : \"\"}`;\r\n\r\n const response =\r\n await api.apps.get<ApiPaginatedActionResult<Subscription>>(url);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao listar assinaturas\",\r\n \"LIST_SUBSCRIPTIONS_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const data = response.data.map((item) => SubscriptionSchema.parse(item));\r\n\r\n return {\r\n data,\r\n total: response.total,\r\n success: true,\r\n } satisfies PaginatedSuccessResult<Subscription>;\r\n }\r\n\r\n /**\r\n * A assinatura corrente do plano da conta, pela regra única do backend\r\n * (`GET .../subscriptions/action/current`): contrato de domínio nunca é\r\n * corrente, encerrada é decidida por `active`. Sem `id_product`, paridade\r\n * com hoje (melhor rank entre todos os produtos, nunca item/domínio).\r\n */\r\n async getCurrentSubscription(\r\n params?: { id_product?: number },\r\n ): Promise<SuccessResult<Subscription | null>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const query = buildQueryParams(\r\n params as Record<string, string | number | undefined>,\r\n );\r\n const url = `/accounts/${id_account}/subscriptions/action/current${query ? `?${query}` : \"\"}`;\r\n\r\n const response = await api.apps.get<ApiPaginatedActionResult<Subscription>>(url);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao consultar assinatura corrente\",\r\n \"GET_CURRENT_SUBSCRIPTION_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const raw = response.data[0];\r\n\r\n return {\r\n success: true,\r\n data: raw ? SubscriptionSchema.parse(raw) : null,\r\n };\r\n }\r\n\r\n async calculateSubscription(\r\n data: CalculateSubscriptionRequest,\r\n ): Promise<SuccessResult<CalculateSubscriptionResponse>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<\r\n ApiActionResult<CalculateSubscriptionResponse>\r\n >(`/accounts/${id_account}/subscriptions/action/calculate`, data);\r\n\r\n console.log(\"Calculate subscription response:\", {\r\n response,\r\n data: JSON.stringify(data, null, 2),\r\n });\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao calcular assinatura\",\r\n \"CALCULATE_SUBSCRIPTION_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n data: CalculateSubscriptionResponseSchema.parse(response.data),\r\n };\r\n }\r\n\r\n async updateSubscriptionPlan(\r\n subscriptionId: number | string,\r\n data: UpdateSubscriptionPlanRequest,\r\n ): Promise<\r\n SuccessResult<UpdateSubscriptionPlanResponse | null> & { client_secret?: string }\r\n > {\r\n await assertManagementPermission('manage_subscriptions');\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.put<\r\n ApiActionResult<UpdateSubscriptionPlanResponse | UpdateSubscriptionPlanResponse[]> & {\r\n client_secret?: string;\r\n }\r\n >(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/plan`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar plano\",\r\n \"UPDATE_SUBSCRIPTION_PLAN_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const raw = Array.isArray(response.data) ? response.data[0] : response.data;\r\n\r\n // Preserva resposta pix-pending (id: null, pending: true, pix: {...})\r\n // pra clientes poderem rotear o user pra fluxo dedicado de pix em vez\r\n // de tratar como subscription comum. Caso contrário, parseia como\r\n // Subscription (compatível com o comportamento antigo de devolver { id }).\r\n let parsed: UpdateSubscriptionPlanResponse | null = null;\r\n if (raw) {\r\n const pixResult = SubscriptionPendingPixResponseSchema.safeParse(raw);\r\n parsed = pixResult.success ? pixResult.data : SubscriptionSchema.parse(raw);\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parsed,\r\n ...(response.client_secret ? { client_secret: response.client_secret } : {}),\r\n };\r\n }\r\n\r\n async updateSubscriptionPayment(\r\n subscriptionId: number | string,\r\n data: { id_card?: string; payment_method_id?: string; payment_method: number },\r\n ): Promise<SuccessResult<void>> {\r\n await assertManagementPermission(\"manage_subscriptions\");\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.put<ApiActionResult<void>>(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/payment`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar método de pagamento\",\r\n \"UPDATE_SUBSCRIPTION_PAYMENT_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return { success: true };\r\n }\r\n\r\n async getFreezeNotice(): Promise<FreezeNotice> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.get<ApiActionResult<FreezeNotice>>(\r\n `/accounts/${id_account}/notices/freeze`,\r\n );\r\n\r\n if (response.status === 0 || !response.data) {\r\n throw new ApiError(\r\n (response.status === 0 ? response.message : \"\") ||\r\n \"Erro ao consultar aviso de congelamento\",\r\n \"GET_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return response.data;\r\n }\r\n\r\n async ackFreezeNotice(cycle: string): Promise<SuccessResult<void>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<ApiActionResult<void>>(\r\n `/accounts/${id_account}/notices/freeze/ack`,\r\n { cycle },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao confirmar aviso de congelamento\",\r\n \"ACK_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return { success: true };\r\n }\r\n}\r\n\r\nexport const subscriptionsService = new SubscriptionsService();\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/services/subscriptions.service.ts"],"sourcesContent":["import \"server-only\";\r\n\r\nimport { api } from \"../../../infra/api/client\";\r\nimport {\r\n ApiError,\r\n ApiActionResult,\r\n ApiPaginatedActionResult,\r\n PaginatedSuccessResult,\r\n SuccessResult,\r\n} from \"../../../infra/api/types\";\r\nimport { buildQueryParams } from \"../../../infra/utils/params\";\r\nimport { assertManagementPermission } from \"../../auth/utils/assert-management-permission\";\r\nimport { getUserContext } from \"../../auth/utils/get-user-context\";\r\nimport {\r\n Subscription,\r\n SubscriptionSchema,\r\n FindSubscriptionsParams,\r\n type ChargeNowResult,\r\n} from \"../types/subscription.type\";\r\nimport {\r\n type CalculateSubscriptionRequest,\r\n type CalculateSubscriptionResponse,\r\n type UpdateSubscriptionPlanRequest,\r\n CalculateSubscriptionResponseSchema,\r\n} from \"../types/calculate-subscription.type\";\r\nimport {\r\n SubscriptionPendingPixResponseSchema,\r\n type SubscriptionPendingPixResponse,\r\n} from \"../types/pix-pending.type\";\r\nimport type { FreezeNotice } from \"../types/freeze-notice.type\";\r\n\r\n/**\r\n * Timeout das mutations de assinatura que ESCREVEM (action/plan e action/payment).\r\n *\r\n * O default do ApiClient é 30s. Essas duas rotas caem no mesmo motor pesado do\r\n * backend (planEdit delega pra paymentEdit) — troca de método de pagamento, mandato\r\n * PIX recorrente Woovi, void-and-replace de fatura — e passam de 30s com alguma\r\n * frequência: em produção elas aparecem como `outcome=canceled` com wallTime ~29.9s.\r\n *\r\n * O problema não é o usuário ver \"Tempo excedido\": é que o abort MATA o worker no\r\n * meio da saga. Se o corte cai depois de criar o mandato Woovi e suprimir a fatura\r\n * de ciclo, sobra mandato órfão + fatura sem dunning, o usuário não recebe QR nenhum\r\n * e a próxima tentativa cria um segundo mandato. Deixar a request terminar converte\r\n * uma escrita parcial numa escrita completa.\r\n *\r\n * Não é a solução definitiva — a rota deveria ser assíncrona (202 + polling no\r\n * correlation_id). É o teto que dá folga enquanto isso.\r\n *\r\n * Só nas mutations: calculateSubscription é idempotente (não escreve nada), então\r\n * um timeout nela não deixa rastro e não precisa esperar mais.\r\n */\r\nconst SUBSCRIPTION_WRITE_TIMEOUT_MS = 60000;\r\n\r\nexport type UpdateSubscriptionPlanResponse =\r\n | Subscription\r\n | SubscriptionPendingPixResponse;\r\n\r\nclass SubscriptionsService {\r\n async listSubscriptions(\r\n params?: Partial<FindSubscriptionsParams>,\r\n ): Promise<PaginatedSuccessResult<Subscription>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const query = buildQueryParams(\r\n params as Record<string, string | boolean | undefined>,\r\n );\r\n const url = `/accounts/${id_account}/subscriptions${query ? `?${query}` : \"\"}`;\r\n\r\n const response =\r\n await api.apps.get<ApiPaginatedActionResult<Subscription>>(url);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao listar assinaturas\",\r\n \"LIST_SUBSCRIPTIONS_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const data = response.data.map((item) => SubscriptionSchema.parse(item));\r\n\r\n return {\r\n data,\r\n total: response.total,\r\n success: true,\r\n } satisfies PaginatedSuccessResult<Subscription>;\r\n }\r\n\r\n async calculateSubscription(\r\n data: CalculateSubscriptionRequest,\r\n ): Promise<SuccessResult<CalculateSubscriptionResponse>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<\r\n ApiActionResult<CalculateSubscriptionResponse>\r\n >(`/accounts/${id_account}/subscriptions/action/calculate`, data);\r\n\r\n console.log(\"Calculate subscription response:\", {\r\n response,\r\n data: JSON.stringify(data, null, 2),\r\n });\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao calcular assinatura\",\r\n \"CALCULATE_SUBSCRIPTION_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n data: CalculateSubscriptionResponseSchema.parse(response.data),\r\n };\r\n }\r\n\r\n async updateSubscriptionPlan(\r\n subscriptionId: number | string,\r\n data: UpdateSubscriptionPlanRequest,\r\n ): Promise<\r\n SuccessResult<UpdateSubscriptionPlanResponse | null> & { client_secret?: string }\r\n > {\r\n await assertManagementPermission('manage_subscriptions');\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.put<\r\n ApiActionResult<UpdateSubscriptionPlanResponse | UpdateSubscriptionPlanResponse[]> & {\r\n client_secret?: string;\r\n }\r\n >(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/plan`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar plano\",\r\n \"UPDATE_SUBSCRIPTION_PLAN_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const raw = Array.isArray(response.data) ? response.data[0] : response.data;\r\n\r\n // Preserva resposta pix-pending (id: null, pending: true, pix: {...})\r\n // pra clientes poderem rotear o user pra fluxo dedicado de pix em vez\r\n // de tratar como subscription comum. Caso contrário, parseia como\r\n // Subscription (compatível com o comportamento antigo de devolver { id }).\r\n let parsed: UpdateSubscriptionPlanResponse | null = null;\r\n if (raw) {\r\n const pixResult = SubscriptionPendingPixResponseSchema.safeParse(raw);\r\n parsed = pixResult.success ? pixResult.data : SubscriptionSchema.parse(raw);\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parsed,\r\n ...(response.client_secret ? { client_secret: response.client_secret } : {}),\r\n };\r\n }\r\n\r\n async updateSubscriptionPayment(\r\n subscriptionId: number | string,\r\n data: {\r\n id_card?: string;\r\n payment_method_id?: string;\r\n payment_method: number;\r\n charge_now?: boolean;\r\n },\r\n ): Promise<\r\n SuccessResult<void> & {\r\n applied: boolean;\r\n client_secret?: string;\r\n charge_now?: ChargeNowResult;\r\n }\r\n > {\r\n await assertManagementPermission(\"manage_subscriptions\");\r\n const { id_account } = await getUserContext();\r\n\r\n /* O `status: 2` do guard de 3DS pendente não cabe em `ApiActionResult`, que só conhece 0 e 1.\r\n * Declarar o terceiro estado aqui é o que permite tratá-lo — sem isso o TypeScript apaga o\r\n * ramo como inalcançável e o caso volta a atravessar como sucesso. */\r\n const response = await api.apps.put<\r\n (ApiActionResult<void> | { status: 2; message?: string }) & {\r\n charge_now?: ChargeNowResult;\r\n client_secret?: string;\r\n }\r\n >(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/payment`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar método de pagamento\",\r\n \"UPDATE_SUBSCRIPTION_PAYMENT_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n /* `status: 2` NÃO é sucesso: o guard de 3DS pendente do paymentEdit devolve esse status sem\r\n * aplicar a troca, e como só o `status === 0` era tratado como erro, a chamada caía no\r\n * `success: true` e a tela dizia \"sua assinatura passou a ser cobrada neste cartão\" para uma\r\n * troca que não aconteceu — descartando junto o `client_secret` que resolveria a pendência.\r\n *\r\n * Por isso o retorno carrega `applied`: quem chama precisa poder distinguir \"trocou\" de\r\n * \"não trocou, conclua o pagamento pendente primeiro\". */\r\n if (response.status === 2) {\r\n return {\r\n success: true,\r\n applied: false,\r\n ...(response.client_secret ? { client_secret: response.client_secret } : {}),\r\n };\r\n }\r\n\r\n /* O desfecho da cobrança imediata precisa atravessar: descartá-lo (como este método fazia com\r\n * o corpo inteiro) apagaria tanto a recusa quanto o `client_secret` do 3DS, e a tela\r\n * anunciaria \"tudo certo\" para uma fatura que continua em aberto. */\r\n return {\r\n success: true,\r\n applied: true,\r\n ...(response.charge_now ? { charge_now: response.charge_now } : {}),\r\n };\r\n }\r\n\r\n async getFreezeNotice(): Promise<FreezeNotice> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.get<ApiActionResult<FreezeNotice>>(\r\n `/accounts/${id_account}/notices/freeze`,\r\n );\r\n\r\n if (response.status === 0 || !response.data) {\r\n throw new ApiError(\r\n (response.status === 0 ? response.message : \"\") ||\r\n \"Erro ao consultar aviso de congelamento\",\r\n \"GET_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return response.data;\r\n }\r\n\r\n async ackFreezeNotice(cycle: string): Promise<SuccessResult<void>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<ApiActionResult<void>>(\r\n `/accounts/${id_account}/notices/freeze/ack`,\r\n { cycle },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao confirmar aviso de congelamento\",\r\n \"ACK_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return { success: true };\r\n }\r\n}\r\n\r\nexport const subscriptionsService = new SubscriptionsService();\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/types/subscription.type.ts"],"sourcesContent":["import z from 'zod';\r\nimport { calendarDateSchema } from '../../../infra/utils/date';\r\n\r\nexport const SubscriptionItemSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_subscription: z.union([z.number(), z.string()]),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().optional(),\r\n value: z.number().nullable(),\r\n currency: z.string(),\r\n quantity: z.number().nullable().transform((v) => v ?? 0),\r\n payment_status: z.number().nullable(),\r\n id_charge: z.union([z.number(), z.string()]).nullable(),\r\n trial: z.boolean().nullable(),\r\n trial_ends_at: z.coerce.date().nullable().optional(),\r\n});\r\n\r\nexport type SubscriptionItem = z.infer<typeof SubscriptionItemSchema>;\r\n\r\nexport const SubscriptionSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_account: z.union([z.number(), z.string()]),\r\n id_card: z.union([z.number(), z.string()]).nullable(),\r\n id_coupon: z.union([z.number(), z.string()]).nullable(),\r\n type: z.string(),\r\n payment_method: z.number().nullable(),\r\n periodicity: z.coerce.number().nullable(),\r\n value: z.number().nullable(),\r\n id_product: z.number(),\r\n id_plan: z.number().nullable(),\r\n plan_name: z.string().nullable().optional(),\r\n id_gateway: z.union([z.number(), z.string()]).nullable(),\r\n gateway_account: z.string().nullable(),\r\n date_hiring: calendarDateSchema().nullable(),\r\n date_due: calendarDateSchema().nullable(),\r\n date_cancellation: calendarDateSchema().nullable(),\r\n date_verification: calendarDateSchema().nullable(),\r\n cancellation_reason: z.string().nullable(),\r\n cancellation_description: z.string().nullable(),\r\n active: z.boolean(),\r\n charged: z.boolean(),\r\n datetime_last_update_periodicity: z.coerce.date().nullable().optional(),\r\n card_name: z.string().nullable().optional(),\r\n card_brand: z.string().nullable().optional(),\r\n card_number: z.string().nullable().optional(),\r\n card: z.object({\r\n id_card: z.union([z.number(), z.string()]).nullable().optional(),\r\n brand: z.string().nullable().optional(),\r\n expires_at: z.string().nullable().optional(),\r\n last4: z.string().nullable().optional(),\r\n name: z.string().nullable().optional(),\r\n }).nullable().optional(),\r\n /* payment_retry: tentativas de cobrança no cartão (Stripe Smart Retries / dunning),\r\n * lidas ao vivo do latest_invoice. null para boleto/pix (sem retries). Ver gapps-r3-api #229. */\r\n payment_retry: z.object({\r\n attempt_count: z.number(),\r\n next_attempt_at: z.string().nullable(),\r\n invoice_status: z.string().nullable(),\r\n }).nullable().optional(),\r\n items: z.array(SubscriptionItemSchema),\r\n pending_change: z.object({\r\n schedule_id: z.string(),\r\n schedule_status: z.string(),\r\n kind: z.string(),\r\n takes_effect_at: z.number().nullable(),\r\n takes_effect_at_iso: z.string().nullable(),\r\n new_plan: z.object({\r\n id: z.number(),\r\n name: z.string(),\r\n periodicity: z.number().nullable(),\r\n value: z.number(),\r\n value_gross: z.number(),\r\n }).nullable(),\r\n new_items: z.array(z.object({\r\n price_id: z.string(),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().nullable(),\r\n quantity: z.number(),\r\n unit_value: z.number(),\r\n value: z.number(),\r\n is_plan_base: z.boolean(),\r\n is_ai_credit: z.boolean(),\r\n })),\r\n new_limits: z.record(z.string(), z.number()).nullable().optional(),\r\n new_discounts: z.array(z.unknown()),\r\n changes: z.array(z.object({\r\n type: z.string(),\r\n id_addon: z.number().nullable().optional(),\r\n addon_name: z.string().nullable().optional(),\r\n quantity: z.number().optional(),\r\n from: z.unknown().optional(),\r\n to: z.unknown().optional(),\r\n })),\r\n }).nullable().optional(),\r\n metadata: z.record(z.string(), z.unknown()).optional(),\r\n});\r\n\r\nexport type Subscription = z.infer<typeof SubscriptionSchema>;\r\n\r\nexport interface FindSubscriptionsParams {\r\n active?: boolean;\r\n trial?: boolean;\r\n date_start?: string;\r\n date_end?: string;\r\n date_hiring_start?: string;\r\n date_hiring_end?: string;\r\n limit?: number;\r\n page?: number;\r\n id_product?: number;\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/types/subscription.type.ts"],"sourcesContent":["import z from 'zod';\r\nimport { calendarDateSchema } from '../../../infra/utils/date';\r\n\r\nexport const SubscriptionItemSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_subscription: z.union([z.number(), z.string()]),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().optional(),\r\n value: z.number().nullable(),\r\n currency: z.string(),\r\n quantity: z.number().nullable().transform((v) => v ?? 0),\r\n payment_status: z.number().nullable(),\r\n id_charge: z.union([z.number(), z.string()]).nullable(),\r\n trial: z.boolean().nullable(),\r\n trial_ends_at: z.coerce.date().nullable().optional(),\r\n});\r\n\r\nexport type SubscriptionItem = z.infer<typeof SubscriptionItemSchema>;\r\n\r\nexport const SubscriptionSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_account: z.union([z.number(), z.string()]),\r\n id_card: z.union([z.number(), z.string()]).nullable(),\r\n id_coupon: z.union([z.number(), z.string()]).nullable(),\r\n type: z.string(),\r\n payment_method: z.number().nullable(),\r\n periodicity: z.coerce.number().nullable(),\r\n value: z.number().nullable(),\r\n id_product: z.number(),\r\n id_plan: z.number().nullable(),\r\n plan_name: z.string().nullable().optional(),\r\n id_gateway: z.union([z.number(), z.string()]).nullable(),\r\n gateway_account: z.string().nullable(),\r\n date_hiring: calendarDateSchema().nullable(),\r\n date_due: calendarDateSchema().nullable(),\r\n date_cancellation: calendarDateSchema().nullable(),\r\n date_verification: calendarDateSchema().nullable(),\r\n cancellation_reason: z.string().nullable(),\r\n cancellation_description: z.string().nullable(),\r\n active: z.boolean(),\r\n charged: z.boolean(),\r\n datetime_last_update_periodicity: z.coerce.date().nullable().optional(),\r\n card_name: z.string().nullable().optional(),\r\n card_brand: z.string().nullable().optional(),\r\n card_number: z.string().nullable().optional(),\r\n card: z.object({\r\n id_card: z.union([z.number(), z.string()]).nullable().optional(),\r\n brand: z.string().nullable().optional(),\r\n expires_at: z.string().nullable().optional(),\r\n last4: z.string().nullable().optional(),\r\n name: z.string().nullable().optional(),\r\n }).nullable().optional(),\r\n /* payment_retry: tentativas de cobrança no cartão (Stripe Smart Retries / dunning),\r\n * lidas ao vivo do latest_invoice. null para boleto/pix (sem retries). Ver gapps-r3-api #229. */\r\n payment_retry: z.object({\r\n attempt_count: z.number(),\r\n next_attempt_at: z.string().nullable(),\r\n invoice_status: z.string().nullable(),\r\n }).nullable().optional(),\r\n items: z.array(SubscriptionItemSchema),\r\n pending_change: z.object({\r\n schedule_id: z.string(),\r\n schedule_status: z.string(),\r\n kind: z.string(),\r\n takes_effect_at: z.number().nullable(),\r\n takes_effect_at_iso: z.string().nullable(),\r\n new_plan: z.object({\r\n id: z.number(),\r\n name: z.string(),\r\n periodicity: z.number().nullable(),\r\n value: z.number(),\r\n value_gross: z.number(),\r\n }).nullable(),\r\n new_items: z.array(z.object({\r\n price_id: z.string(),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().nullable(),\r\n quantity: z.number(),\r\n unit_value: z.number(),\r\n value: z.number(),\r\n is_plan_base: z.boolean(),\r\n is_ai_credit: z.boolean(),\r\n })),\r\n new_limits: z.record(z.string(), z.number()).nullable().optional(),\r\n new_discounts: z.array(z.unknown()),\r\n changes: z.array(z.object({\r\n type: z.string(),\r\n id_addon: z.number().nullable().optional(),\r\n addon_name: z.string().nullable().optional(),\r\n quantity: z.number().optional(),\r\n from: z.unknown().optional(),\r\n to: z.unknown().optional(),\r\n })),\r\n }).nullable().optional(),\r\n metadata: z.record(z.string(), z.unknown()).optional(),\r\n});\r\n\r\nexport type Subscription = z.infer<typeof SubscriptionSchema>;\r\n\r\n/* Desfecho da cobrança imediata pedida com `charge_now` na troca de forma de pagamento\r\n * (gapps-r3-api, PUT subscriptions/:id/action/payment).\r\n *\r\n * Vem separado do `status` da resposta de propósito: quando isto chega, a troca JÁ está firmada\r\n * no banco e na Stripe. Uma cobrança recusada aqui não desfaz a troca — o dunning da Stripe segue\r\n * armado no cartão novo —, então a tela precisa contar as duas coisas em separado.\r\n *\r\n * `outcome` é a ÚNICA fonte da verdade sobre o que aconteceu, e a tela deve falar só a partir dele.\r\n * Substituiu um par de booleanos (`attempted`/`paid`) que não distinguia recusa de 3DS pendente: a\r\n * tela lia `paid: false` depois de um desafio bem-sucedido e anunciava falha para um pagamento que\r\n * tinha acabado de completar.\r\n *\r\n * `amount` vem em centavos, na `currency` da fatura, e é o valor REALMENTE cobrado — não uma\r\n * previsão feita antes da chamada. */\r\nexport interface ChargeNowResult {\r\n outcome: 'charged' | 'requires_action' | 'declined' | 'none';\r\n invoice_id?: string;\r\n amount?: number | null;\r\n currency?: string | null;\r\n client_secret?: string;\r\n /* Presente em `declined`: a razão que o emissor deu. */\r\n message?: string;\r\n /* Presente em `none`: por que não houve o que cobrar (`sem_fatura_de_ciclo_aberta`,\r\n * `deteccao_falhou`, `sem_contexto_de_cobranca`). */\r\n reason?: string;\r\n}\r\n\r\nexport interface FindSubscriptionsParams {\r\n active?: boolean;\r\n trial?: boolean;\r\n date_start?: string;\r\n date_end?: string;\r\n date_hiring_start?: string;\r\n date_hiring_end?: string;\r\n limit?: number;\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toCalendarDate, daysBetween } from "../../../infra/utils/date";
|
|
2
2
|
import { getSubscriptionCancellationState } from "./get-subscription-cancellation-state";
|
|
3
|
-
|
|
3
|
+
const FREE_PLAN_ID = 5;
|
|
4
4
|
function isOverdue(subscription) {
|
|
5
5
|
const retry = subscription.payment_retry;
|
|
6
6
|
if (retry && retry.invoice_status === "open" && retry.attempt_count > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-active-paid-subscription.ts"],"sourcesContent":["import { toCalendarDate, daysBetween } from \"../../../infra/utils/date\";\nimport { getSubscriptionCancellationState } from \"./get-subscription-cancellation-state\";\nimport
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-active-paid-subscription.ts"],"sourcesContent":["import { toCalendarDate, daysBetween } from \"../../../infra/utils/date\";\nimport { getSubscriptionCancellationState } from \"./get-subscription-cancellation-state\";\nimport type { Subscription } from \"../types/subscription.type\";\n\nconst FREE_PLAN_ID = 5;\n\ntype SubscriptionSnapshot = Pick<\n Subscription,\n | \"type\"\n | \"active\"\n | \"id_plan\"\n | \"date_due\"\n | \"date_cancellation\"\n | \"payment_retry\"\n>;\n\n/**\n * Fatura vencida e não paga. Mesmos sinais do SubscriptionBanner:\n * - cartão: dunning do Stripe (Smart Retries) — `invoice_status === 'open'`\n * com tentativa já feita.\n * - boleto/pix (sem retries): `date_due` no passado, ou seja a renovação não\n * foi paga.\n */\nfunction isOverdue(subscription: SubscriptionSnapshot): boolean {\n const retry = subscription.payment_retry;\n if (retry && retry.invoice_status === \"open\" && retry.attempt_count > 0) {\n return true;\n }\n\n const dueDate = toCalendarDate(subscription.date_due);\n const today = toCalendarDate(new Date());\n if (!dueDate || !today) return false;\n\n return daysBetween(dueDate, today) > 0;\n}\n\n/**\n * A conta já tem assinatura paga vigente — contratar outra viraria cobrança\n * duplicada (ver Asana ID-4743). O caminho correto pra ela é trocar de plano\n * ou reativar, não passar pelo checkout de novo.\n *\n * Retorna `false` (ou seja, PODE contratar) para quem legitimamente precisa do\n * checkout mesmo já tendo uma linha de assinatura:\n * - sem assinatura;\n * - `active === false` (baixada, inclusive por falha de pagamento);\n * - free (`type` ou plano legado 5) e trial — são justamente os que fazem upgrade;\n * - cancelada cujo período já terminou (`getSubscriptionCancellationState`);\n * - em atraso — precisa contratar pra regularizar.\n *\n * Cancelamento ainda dentro do período (`pending`) conta como vigente e\n * bloqueia: o serviço continua ativo e pago, e o caminho é reativar.\n */\nexport function hasActivePaidSubscription(\n subscription: SubscriptionSnapshot | undefined | null,\n): boolean {\n if (!subscription) return false;\n if (!subscription.active) return false;\n if (subscription.type === \"free\" || subscription.id_plan === FREE_PLAN_ID) {\n return false;\n }\n if (subscription.type === \"trial\") return false;\n if (getSubscriptionCancellationState(subscription) === \"cancelled\") {\n return false;\n }\n if (isOverdue(subscription)) return false;\n\n return true;\n}\n"],"mappings":"AAAA,SAAS,gBAAgB,mBAAmB;AAC5C,SAAS,wCAAwC;AAGjD,MAAM,eAAe;AAmBrB,SAAS,UAAU,cAA6C;AAC9D,QAAM,QAAQ,aAAa;AAC3B,MAAI,SAAS,MAAM,mBAAmB,UAAU,MAAM,gBAAgB,GAAG;AACvE,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,eAAe,aAAa,QAAQ;AACpD,QAAM,QAAQ,eAAe,oBAAI,KAAK,CAAC;AACvC,MAAI,CAAC,WAAW,CAAC,MAAO,QAAO;AAE/B,SAAO,YAAY,SAAS,KAAK,IAAI;AACvC;AAkBO,SAAS,0BACd,cACS;AACT,MAAI,CAAC,aAAc,QAAO;AAC1B,MAAI,CAAC,aAAa,OAAQ,QAAO;AACjC,MAAI,aAAa,SAAS,UAAU,aAAa,YAAY,cAAc;AACzE,WAAO;AAAA,EACT;AACA,MAAI,aAAa,SAAS,QAAS,QAAO;AAC1C,MAAI,iCAAiC,YAAY,MAAM,aAAa;AAClE,WAAO;AAAA,EACT;AACA,MAAI,UAAU,YAAY,EAAG,QAAO;AAEpC,SAAO;AACT;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-paid-subscription.ts"],"sourcesContent":["import type { Subscription } from '../types/subscription.type';\
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-paid-subscription.ts"],"sourcesContent":["import type { Subscription } from '../types/subscription.type';\n\nconst FREE_PLAN_ID = 5;\n\ntype PaidSubscriptionSnapshot = Pick<Subscription, 'type' | 'id_plan' | 'charged'>;\n\n/**\n * A conta já teve pagamento confirmado numa assinatura paga.\n *\n * Diferente de `hasActivePaidSubscription`, aqui NÃO importa se a assinatura\n * segue vigente: quem pagou e depois cancelou/atrasou continua precisando de\n * nota fiscal do que já foi cobrado. Por isso o sinal é `charged` (marcado pelo\n * backend no `bill_paid`), e não `active`/`date_due`.\n *\n * Free e trial ficam de fora — nunca houve cobrança a documentar.\n */\nexport function hasPaidSubscription(\n subscription: PaidSubscriptionSnapshot | undefined | null,\n): boolean {\n if (!subscription) return false;\n if (subscription.type === 'free' || subscription.id_plan === FREE_PLAN_ID) {\n return false;\n }\n if (subscription.type === 'trial') return false;\n\n return subscription.charged === true;\n}\n"],"mappings":"AAEA,MAAM,eAAe;AAcd,SAAS,oBACd,cACS;AACT,MAAI,CAAC,aAAc,QAAO;AAC1B,MAAI,aAAa,SAAS,UAAU,aAAa,YAAY,cAAc;AACzE,WAAO;AAAA,EACT;AACA,MAAI,aAAa,SAAS,QAAS,QAAO;AAE1C,SAAO,aAAa,YAAY;AAClC;","names":[]}
|
package/dist/server.mjs
CHANGED
|
@@ -22,8 +22,6 @@ import { findUserById } from "./modules/users/action/find-user-by-id.action";
|
|
|
22
22
|
import { findCurrentAccount } from "./modules/accounts/actions/find-current-account.action";
|
|
23
23
|
import { getAccountTokenAction } from "./modules/accounts/actions/get-account-token.action";
|
|
24
24
|
import { listSubscriptionsAction } from "./modules/subscriptions/actions/list-subscriptions.action";
|
|
25
|
-
import { getCurrentSubscriptionAction } from "./modules/subscriptions/actions/get-current-subscription.action";
|
|
26
|
-
import { readCurrentSubscription } from "./modules/subscriptions/utils/read-current-subscription";
|
|
27
25
|
import { ackFreezeNoticeAction } from "./modules/subscriptions/actions/ack-freeze-notice.action";
|
|
28
26
|
import { calculateSubscriptionAction } from "./modules/subscriptions/actions/calculate-subscription.action";
|
|
29
27
|
import { updateSubscriptionPlanAction } from "./modules/subscriptions/actions/update-subscription-plan.action";
|
|
@@ -112,7 +110,6 @@ export {
|
|
|
112
110
|
generateTwoFactorAction,
|
|
113
111
|
getAccountTokenAction,
|
|
114
112
|
getClientInfoFromRequest,
|
|
115
|
-
getCurrentSubscriptionAction,
|
|
116
113
|
getUserContext,
|
|
117
114
|
hasActivePaidSubscription,
|
|
118
115
|
hasSubscriptionExpired,
|
|
@@ -126,7 +123,6 @@ export {
|
|
|
126
123
|
processImageAction,
|
|
127
124
|
projectUsersService,
|
|
128
125
|
projectsService,
|
|
129
|
-
readCurrentSubscription,
|
|
130
126
|
removeProjectUserAction,
|
|
131
127
|
requestEmailChangeAction,
|
|
132
128
|
requestPhoneChangeAction,
|
package/dist/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { assertAccountNotFrozen } from './modules/auth/utils/assert-account-not-frozen';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { chargesService } from './modules/charges/services/charges.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { socialGoogleLoginAction } from './modules/auth/actions/social-google-login.action';\r\nexport { socialGoogleOnboardingAction } from './modules/auth/actions/social-google-onboarding.action';\r\nexport { unlinkGoogleAction } from './modules/auth/actions/unlink-google.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport {
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { assertAccountNotFrozen } from './modules/auth/utils/assert-account-not-frozen';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { chargesService } from './modules/charges/services/charges.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { socialGoogleLoginAction } from './modules/auth/actions/social-google-login.action';\r\nexport { socialGoogleOnboardingAction } from './modules/auth/actions/social-google-onboarding.action';\r\nexport { unlinkGoogleAction } from './modules/auth/actions/unlink-google.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { ackFreezeNoticeAction } from './modules/subscriptions/actions/ack-freeze-notice.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { createSetupIntentAction } from './modules/cards/actions/create-setup-intent.action';\r\nexport { chargeActionAction } from './modules/charges/actions/charge-action.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { hasActivePaidSubscription } from './modules/subscriptions/utils/has-active-paid-subscription';\r\n\r\n// Project Services & Actions (server-only)\r\nexport { projectsService } from './modules/projects/services/projects.service';\r\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\r\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\r\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\r\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\r\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\r\n\r\n// Project Users Services & Actions (server-only)\r\nexport { projectUsersService } from './modules/projects/services/project-users.service';\r\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\r\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\r\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\r\n\r\n// Account Services & Actions (server-only)\r\nexport { accountService } from './modules/accounts/services/account.service';\r\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\r\nexport {\r\n listAccountUsersAction,\r\n updateUserAction,\r\n updateAccountAction,\r\n updateAccountUserByIdAction,\r\n updateBillingDataAction,\r\n deleteAccountUserAction,\r\n deleteAccountAction,\r\n createAccountUserAction,\r\n changePasswordAction,\r\n requestEmailChangeAction,\r\n confirmEmailChangeAction,\r\n requestPhoneChangeAction,\r\n confirmPhoneChangeAction,\r\n generateTwoFactorAction,\r\n confirmTwoFactorAction,\r\n disableTwoFactorAction,\r\n} from './modules/accounts/actions/account-management.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\nexport { resolveLocale } from './i18n/resolve-locale';\r\nexport type { ResolveLocaleOptions } from './i18n/resolve-locale';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\r\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\r\nexport { safeServerAction } from './utils/safeServerAction';\r\nexport { safeMutationAction } from './utils/safeMutationAction';\r\n\r\n// Image Processing (server-side)\r\nexport { processImageAction } from './modules/images/actions/process-image.action';\r\nexport { processImage } from './modules/images/services/image-processing.service';\r\nexport type {\r\n ProcessImageInput,\r\n ProcessImageOutput,\r\n ProcessImageActionInput,\r\n ProcessImageActionOutput,\r\n} from './modules/images/types/image.type';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,iCAAiC;AAG1C,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAE9B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
2
|
import { readStorage, writeStorage } from "../utils/browser/safe-storage";
|
|
4
3
|
const STORAGE_KEY = "md-sidebar-expanded";
|
|
4
|
+
function getInitialState() {
|
|
5
|
+
return readStorage(STORAGE_KEY) === "true";
|
|
6
|
+
}
|
|
5
7
|
const useMdSidebarStore = create((set) => ({
|
|
6
|
-
isExpanded:
|
|
7
|
-
_hydrated: false,
|
|
8
|
+
isExpanded: getInitialState(),
|
|
8
9
|
toggle: () => set((state) => {
|
|
9
10
|
const next = !state.isExpanded;
|
|
10
11
|
writeStorage(STORAGE_KEY, String(next));
|
|
@@ -17,23 +18,9 @@ const useMdSidebarStore = create((set) => ({
|
|
|
17
18
|
collapse: () => {
|
|
18
19
|
writeStorage(STORAGE_KEY, "false");
|
|
19
20
|
return set({ isExpanded: false });
|
|
20
|
-
},
|
|
21
|
-
_hydrate: () => {
|
|
22
|
-
const value = readStorage(STORAGE_KEY) === "true";
|
|
23
|
-
set({ isExpanded: value, _hydrated: true });
|
|
24
21
|
}
|
|
25
22
|
}));
|
|
26
|
-
function useMdSidebarHydration() {
|
|
27
|
-
const hydrate = useMdSidebarStore((s) => s._hydrate);
|
|
28
|
-
const hydrated = useMdSidebarStore((s) => s._hydrated);
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
if (!hydrated) {
|
|
31
|
-
hydrate();
|
|
32
|
-
}
|
|
33
|
-
}, [hydrate, hydrated]);
|
|
34
|
-
}
|
|
35
23
|
export {
|
|
36
|
-
useMdSidebarHydration,
|
|
37
24
|
useMdSidebarStore
|
|
38
25
|
};
|
|
39
26
|
//# sourceMappingURL=useMdSidebarStore.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/store/useMdSidebarStore.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/store/useMdSidebarStore.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { readStorage, writeStorage } from \"../utils/browser/safe-storage\";\n\nconst STORAGE_KEY = \"md-sidebar-expanded\";\n\nfunction getInitialState(): boolean {\n return readStorage(STORAGE_KEY) === \"true\";\n}\n\ntype MdSidebarStore = {\n isExpanded: boolean;\n toggle: () => void;\n expand: () => void;\n collapse: () => void;\n};\n\nexport const useMdSidebarStore = create<MdSidebarStore>((set) => ({\n isExpanded: getInitialState(),\n toggle: () => set((state) => {\n const next = !state.isExpanded;\n writeStorage(STORAGE_KEY, String(next));\n return { isExpanded: next };\n }),\n expand: () => {\n writeStorage(STORAGE_KEY, \"true\");\n return set({ isExpanded: true });\n },\n collapse: () => {\n writeStorage(STORAGE_KEY, \"false\");\n return set({ isExpanded: false });\n },\n}));\n"],"mappings":"AAAA,SAAS,cAAc;AACvB,SAAS,aAAa,oBAAoB;AAE1C,MAAM,cAAc;AAEpB,SAAS,kBAA2B;AAChC,SAAO,YAAY,WAAW,MAAM;AACxC;AASO,MAAM,oBAAoB,OAAuB,CAAC,SAAS;AAAA,EAC9D,YAAY,gBAAgB;AAAA,EAC5B,QAAQ,MAAM,IAAI,CAAC,UAAU;AACzB,UAAM,OAAO,CAAC,MAAM;AACpB,iBAAa,aAAa,OAAO,IAAI,CAAC;AACtC,WAAO,EAAE,YAAY,KAAK;AAAA,EAC9B,CAAC;AAAA,EACD,QAAQ,MAAM;AACV,iBAAa,aAAa,MAAM;AAChC,WAAO,IAAI,EAAE,YAAY,KAAK,CAAC;AAAA,EACnC;AAAA,EACA,UAAU,MAAM;AACZ,iBAAa,aAAa,OAAO;AACjC,WAAO,IAAI,EAAE,YAAY,MAAM,CAAC;AAAA,EACpC;AACJ,EAAE;","names":[]}
|
|
@@ -6,10 +6,7 @@ async function safeServerAction(fn) {
|
|
|
6
6
|
return { success: true, data };
|
|
7
7
|
} catch (error) {
|
|
8
8
|
if (isRedirectError(error)) throw error;
|
|
9
|
-
|
|
10
|
-
return { success: false, error: error.message, code: error.code, statusCode: error.statusCode };
|
|
11
|
-
}
|
|
12
|
-
const message = error instanceof Error ? error.message : "Erro desconhecido";
|
|
9
|
+
const message = error instanceof ApiError ? error.message : error instanceof Error ? error.message : "Erro desconhecido";
|
|
13
10
|
return { success: false, error: message };
|
|
14
11
|
}
|
|
15
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/safeServerAction.ts"],"sourcesContent":["import { isRedirectError } from 'next/dist/client/components/redirect-error';\nimport { ApiError } from '../infra/api/types';\nimport type { ActionResult } from '../infra/api/types';\n\nexport async function safeServerAction<T>(\n fn: () => Promise<T>\n): Promise<ActionResult<T>> {\n try {\n const data = await fn();\n return { success: true, data };\n } catch (error) {\n if (isRedirectError(error)) throw error;\n
|
|
1
|
+
{"version":3,"sources":["../../src/utils/safeServerAction.ts"],"sourcesContent":["import { isRedirectError } from 'next/dist/client/components/redirect-error';\nimport { ApiError } from '../infra/api/types';\nimport type { ActionResult } from '../infra/api/types';\n\nexport async function safeServerAction<T>(\n fn: () => Promise<T>\n): Promise<ActionResult<T>> {\n try {\n const data = await fn();\n return { success: true, data };\n } catch (error) {\n if (isRedirectError(error)) throw error;\n const message =\n error instanceof ApiError\n ? error.message\n : error instanceof Error\n ? error.message\n : 'Erro desconhecido';\n return { success: false, error: message };\n }\n}\n"],"mappings":"AAAA,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AAGzB,eAAsB,iBACpB,IAC0B;AAC1B,MAAI;AACF,UAAM,OAAO,MAAM,GAAG;AACtB,WAAO,EAAE,SAAS,MAAM,KAAK;AAAA,EAC/B,SAAS,OAAO;AACd,QAAI,gBAAgB,KAAK,EAAG,OAAM;AAClC,UAAM,UACJ,iBAAiB,WACb,MAAM,UACN,iBAAiB,QACf,MAAM,UACN;AACR,WAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AAAA,EAC1C;AACF;","names":[]}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { ApiError } from "../infra/api/types";
|
|
2
1
|
function withAction(action) {
|
|
3
2
|
return async (...args) => {
|
|
4
3
|
const result = await action(...args);
|
|
5
4
|
if (!result.success) {
|
|
6
|
-
throw new
|
|
5
|
+
throw new Error(result.error ?? "Erro desconhecido");
|
|
7
6
|
}
|
|
8
7
|
return result.data;
|
|
9
8
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/withAction.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../src/utils/withAction.ts"],"sourcesContent":["import type { ActionResult } from '../infra/api/types';\n\nexport function withAction<TArgs extends any[], TData>(\n action: (...args: TArgs) => Promise<ActionResult<TData>>\n): (...args: TArgs) => Promise<TData> {\n return async (...args) => {\n const result = await action(...args);\n if (!result.success) {\n throw new Error(result.error ?? 'Erro desconhecido');\n }\n return result.data as TData;\n };\n}\n"],"mappings":"AAEO,SAAS,WACd,QACoC;AACpC,SAAO,UAAU,SAAS;AACxB,UAAM,SAAS,MAAM,OAAO,GAAG,IAAI;AACnC,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,MAAM,OAAO,SAAS,mBAAmB;AAAA,IACrD;AACA,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
import Image from "next/image";
|
|
5
|
-
import { useTranslations } from "next-intl";
|
|
6
5
|
import { IconMenu2, IconX } from "@tabler/icons-react";
|
|
7
6
|
import { ProfilePopover } from "./ProfilePopover";
|
|
8
|
-
import { GreatDomainIcon } from "./GreatDomainIcon";
|
|
9
7
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/overlay/Tooltip";
|
|
10
8
|
import { useMobileNavbarSheet } from "../../store/useMobileNavbarSheet";
|
|
11
9
|
import {
|
|
@@ -17,13 +15,11 @@ import type { ProfileMenuItem } from "./ProfilePopover";
|
|
|
17
15
|
|
|
18
16
|
export interface AppMobileNavBarProps {
|
|
19
17
|
/** Which app is currently active — controls which icon gets the indicator */
|
|
20
|
-
activeApp?: "gapps" | "gpages"
|
|
18
|
+
activeApp?: "gapps" | "gpages";
|
|
21
19
|
/** Called when the GreatApps logo is clicked */
|
|
22
20
|
onLogoClick?: () => void;
|
|
23
21
|
/** Called when the app icon (e.g. GreatPages) is clicked */
|
|
24
22
|
onAppIconClick?: () => void;
|
|
25
|
-
/** Called when the GreatDomain icon is clicked; without it the icon is not rendered (app flag) */
|
|
26
|
-
onDomainIconClick?: () => void;
|
|
27
23
|
/** Extra slot rendered after the app icon (e.g. project selector) */
|
|
28
24
|
extraLeftSlot?: ReactNode;
|
|
29
25
|
menuItems?: ProfileMenuItem[];
|
|
@@ -34,12 +30,10 @@ export function AppMobileNavBar({
|
|
|
34
30
|
activeApp,
|
|
35
31
|
onLogoClick,
|
|
36
32
|
onAppIconClick,
|
|
37
|
-
onDomainIconClick,
|
|
38
33
|
extraLeftSlot,
|
|
39
34
|
menuItems = [],
|
|
40
35
|
showMenuToggle = true,
|
|
41
36
|
}: AppMobileNavBarProps) {
|
|
42
|
-
const translate = useTranslations();
|
|
43
37
|
const { isOpen, toggle } = useMobileNavbarSheet();
|
|
44
38
|
const { whitelabel } = useWhitelabel();
|
|
45
39
|
const isDefaultWl = useIsDefaultWhitelabel();
|
|
@@ -48,7 +42,6 @@ export function AppMobileNavBar({
|
|
|
48
42
|
|
|
49
43
|
const isGappsActive = activeApp === "gapps";
|
|
50
44
|
const isGpagesActive = activeApp === "gpages";
|
|
51
|
-
const isGdomainActive = activeApp === "gdomain";
|
|
52
45
|
|
|
53
46
|
const wlIcon = whitelabel?.secondary_logo ? (
|
|
54
47
|
<Image
|
|
@@ -87,9 +80,9 @@ export function AppMobileNavBar({
|
|
|
87
80
|
|
|
88
81
|
return (
|
|
89
82
|
<div className="fixed top-0 left-0 right-0 flex md:hidden items-center justify-between p-4 bg-white z-50 border-b border-gray-200">
|
|
90
|
-
<div className="flex items-center gap-1.5
|
|
83
|
+
<div className="flex items-center gap-1.5">
|
|
91
84
|
{/* GreatApps logo */}
|
|
92
|
-
<div className="relative
|
|
85
|
+
<div className="relative">
|
|
93
86
|
<Tooltip>
|
|
94
87
|
<TooltipTrigger asChild>
|
|
95
88
|
<div
|
|
@@ -126,7 +119,7 @@ export function AppMobileNavBar({
|
|
|
126
119
|
</div>
|
|
127
120
|
|
|
128
121
|
{/* GreatPages icon */}
|
|
129
|
-
<div className="relative
|
|
122
|
+
<div className="relative">
|
|
130
123
|
<Tooltip>
|
|
131
124
|
<TooltipTrigger asChild>
|
|
132
125
|
<div
|
|
@@ -194,86 +187,9 @@ export function AppMobileNavBar({
|
|
|
194
187
|
/>
|
|
195
188
|
</div>
|
|
196
189
|
|
|
197
|
-
{
|
|
198
|
-
{onDomainIconClick ? (
|
|
199
|
-
<div className="relative shrink-0">
|
|
200
|
-
<Tooltip>
|
|
201
|
-
<TooltipTrigger asChild>
|
|
202
|
-
<div
|
|
203
|
-
aria-label={
|
|
204
|
-
isDefaultWl
|
|
205
|
-
? translate("common.layout.navbar.greatDomain")
|
|
206
|
-
: translate("common.layout.navbar.domains")
|
|
207
|
-
}
|
|
208
|
-
className={cn(
|
|
209
|
-
"size-10 rounded-[10px] flex items-center justify-center border-2 ring-2 transition-colors duration-300",
|
|
210
|
-
isGdomainActive
|
|
211
|
-
? isDefaultWl
|
|
212
|
-
? "bg-[#EDE9FE] border-white ring-[#C092FF]"
|
|
213
|
-
: "border-white ring-transparent"
|
|
214
|
-
: isDefaultWl
|
|
215
|
-
? "hover:bg-gray-100 border-transparent ring-transparent"
|
|
216
|
-
: "border-transparent ring-transparent",
|
|
217
|
-
"cursor-pointer",
|
|
218
|
-
)}
|
|
219
|
-
style={
|
|
220
|
-
!isDefaultWl
|
|
221
|
-
? {
|
|
222
|
-
backgroundColor: primaryColor,
|
|
223
|
-
boxShadow: isGdomainActive
|
|
224
|
-
? `0 0 0 2px ${primaryColor}40`
|
|
225
|
-
: undefined,
|
|
226
|
-
opacity: !isGdomainActive ? 0.2 : undefined,
|
|
227
|
-
}
|
|
228
|
-
: undefined
|
|
229
|
-
}
|
|
230
|
-
onClick={onDomainIconClick}
|
|
231
|
-
>
|
|
232
|
-
{isDefaultWl ? (
|
|
233
|
-
<GreatDomainIcon active={isGdomainActive} />
|
|
234
|
-
) : (
|
|
235
|
-
<svg
|
|
236
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
237
|
-
width="24"
|
|
238
|
-
height="24"
|
|
239
|
-
viewBox="0 0 24 24"
|
|
240
|
-
fill="none"
|
|
241
|
-
stroke="white"
|
|
242
|
-
strokeWidth="2"
|
|
243
|
-
strokeLinecap="round"
|
|
244
|
-
strokeLinejoin="round"
|
|
245
|
-
>
|
|
246
|
-
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" />
|
|
247
|
-
<path d="M3.6 9h16.8" />
|
|
248
|
-
<path d="M3.6 15h16.8" />
|
|
249
|
-
<path d="M11.5 3a17 17 0 0 0 0 18" />
|
|
250
|
-
<path d="M12.5 3a17 17 0 0 1 0 18" />
|
|
251
|
-
</svg>
|
|
252
|
-
)}
|
|
253
|
-
</div>
|
|
254
|
-
</TooltipTrigger>
|
|
255
|
-
<TooltipContent side="bottom">
|
|
256
|
-
{isDefaultWl
|
|
257
|
-
? translate("common.layout.navbar.greatDomain")
|
|
258
|
-
: translate("common.layout.navbar.domains")}
|
|
259
|
-
</TooltipContent>
|
|
260
|
-
</Tooltip>
|
|
261
|
-
<div
|
|
262
|
-
className={cn(
|
|
263
|
-
"absolute -bottom-[17px] left-1/2 -translate-x-1/2 w-5 h-[3px] rounded-t-xl transition-opacity duration-300",
|
|
264
|
-
isDefaultWl ? "bg-[#984DFF]" : "",
|
|
265
|
-
isGdomainActive ? "opacity-100" : "opacity-0",
|
|
266
|
-
)}
|
|
267
|
-
style={!isDefaultWl ? { backgroundColor: primaryColor } : undefined}
|
|
268
|
-
/>
|
|
269
|
-
</div>
|
|
270
|
-
) : null}
|
|
271
|
-
|
|
272
|
-
{extraLeftSlot ? (
|
|
273
|
-
<div className="min-w-0 shrink [&>*]:max-w-full">{extraLeftSlot}</div>
|
|
274
|
-
) : null}
|
|
190
|
+
{extraLeftSlot}
|
|
275
191
|
</div>
|
|
276
|
-
<div className="flex items-center gap-1.5
|
|
192
|
+
<div className="flex items-center gap-1.5">
|
|
277
193
|
<ProfilePopover
|
|
278
194
|
side="bottom"
|
|
279
195
|
align="end"
|