@greatapps/common 1.1.237 → 1.1.239
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/modals/BuyCreditsModal.mjs +277 -0
- package/dist/components/modals/BuyCreditsModal.mjs.map +1 -0
- package/dist/components/navigation/subcomponents/CreditsCard.mjs +5 -2
- package/dist/components/navigation/subcomponents/CreditsCard.mjs.map +1 -1
- package/dist/components/ui/data-display/CardBrandIcons.mjs +484 -0
- package/dist/components/ui/data-display/CardBrandIcons.mjs.map +1 -0
- package/dist/components/ui/data-display/CardItem.mjs +67 -0
- package/dist/components/ui/data-display/CardItem.mjs.map +1 -0
- package/dist/components/ui/data-display/PaymentInfoCard.mjs +161 -0
- package/dist/components/ui/data-display/PaymentInfoCard.mjs.map +1 -0
- package/dist/components/ui/feedback/Skeleton.mjs +9 -0
- package/dist/components/ui/feedback/Skeleton.mjs.map +1 -0
- package/dist/index.mjs +57 -29
- package/dist/index.mjs.map +1 -1
- package/dist/modules/cards/actions/list-cards.action.mjs +10 -0
- package/dist/modules/cards/actions/list-cards.action.mjs.map +1 -0
- package/dist/modules/cards/hooks/cards.hook.mjs +16 -0
- package/dist/modules/cards/hooks/cards.hook.mjs.map +1 -0
- package/dist/modules/cards/services/cards.service.mjs +29 -0
- package/dist/modules/cards/services/cards.service.mjs.map +1 -0
- package/dist/modules/cards/types.mjs +27 -0
- package/dist/modules/cards/types.mjs.map +1 -0
- package/dist/modules/subscriptions/actions/calculate-subscription.action.mjs +27 -0
- package/dist/modules/subscriptions/actions/calculate-subscription.action.mjs.map +1 -0
- package/dist/modules/subscriptions/actions/update-subscription-plan.action.mjs +21 -0
- package/dist/modules/subscriptions/actions/update-subscription-plan.action.mjs.map +1 -0
- package/dist/modules/subscriptions/constants/addons.constants.mjs +13 -1
- package/dist/modules/subscriptions/constants/addons.constants.mjs.map +1 -1
- package/dist/modules/subscriptions/hooks/calculate-subscription.hook.mjs +13 -0
- package/dist/modules/subscriptions/hooks/calculate-subscription.hook.mjs.map +1 -0
- package/dist/modules/subscriptions/hooks/update-subscription-plan.hook.mjs +22 -0
- package/dist/modules/subscriptions/hooks/update-subscription-plan.hook.mjs.map +1 -0
- package/dist/modules/subscriptions/services/subscriptions.service.mjs +36 -0
- package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -1
- package/dist/modules/subscriptions/types/billing-period.type.mjs +1 -0
- package/dist/modules/subscriptions/types/billing-period.type.mjs.map +1 -0
- package/dist/modules/subscriptions/types/calculate-subscription.type.mjs +72 -0
- package/dist/modules/subscriptions/types/calculate-subscription.type.mjs.map +1 -0
- package/dist/modules/subscriptions/utils/build-plan-extras.mjs +34 -0
- package/dist/modules/subscriptions/utils/build-plan-extras.mjs.map +1 -0
- package/dist/modules/subscriptions/utils/periodicity.mjs +48 -0
- package/dist/modules/subscriptions/utils/periodicity.mjs.map +1 -0
- package/dist/modules/subscriptions/utils/resolve-plan-extras-prices.mjs +14 -0
- package/dist/modules/subscriptions/utils/resolve-plan-extras-prices.mjs.map +1 -0
- package/dist/server.mjs +10 -0
- package/dist/server.mjs.map +1 -1
- package/dist/store/useBuyCreditsModal.mjs +18 -0
- package/dist/store/useBuyCreditsModal.mjs.map +1 -0
- package/package.json +1 -1
- package/src/components/modals/BuyCreditsModal.tsx +304 -0
- package/src/components/navigation/subcomponents/CreditsCard.tsx +5 -2
- package/src/components/ui/data-display/CardBrandIcons.tsx +336 -0
- package/src/components/ui/data-display/CardItem.tsx +75 -0
- package/src/components/ui/data-display/PaymentInfoCard.tsx +186 -0
- package/src/components/ui/feedback/Skeleton.tsx +5 -0
- package/src/index.ts +21 -2
- package/src/modules/cards/actions/list-cards.action.ts +8 -0
- package/src/modules/cards/hooks/cards.hook.ts +14 -0
- package/src/modules/cards/services/cards.service.ts +32 -0
- package/src/modules/cards/types.ts +32 -0
- package/src/modules/subscriptions/actions/calculate-subscription.action.ts +32 -0
- package/src/modules/subscriptions/actions/update-subscription-plan.action.ts +26 -0
- package/src/modules/subscriptions/constants/addons.constants.ts +18 -0
- package/src/modules/subscriptions/hooks/calculate-subscription.hook.ts +13 -0
- package/src/modules/subscriptions/hooks/update-subscription-plan.hook.ts +25 -0
- package/src/modules/subscriptions/services/subscriptions.service.ts +52 -1
- package/src/modules/subscriptions/types/billing-period.type.ts +1 -0
- package/src/modules/subscriptions/types/calculate-subscription.type.ts +75 -0
- package/src/modules/subscriptions/utils/build-plan-extras.ts +53 -0
- package/src/modules/subscriptions/utils/periodicity.ts +52 -0
- package/src/modules/subscriptions/utils/resolve-plan-extras-prices.ts +23 -0
- package/src/server.ts +5 -0
- package/src/store/useBuyCreditsModal.ts +25 -0
package/src/index.ts
CHANGED
|
@@ -43,6 +43,9 @@ export {
|
|
|
43
43
|
export { useActiveSubscription } from "./modules/subscriptions/hooks/find-active-subscription.hook";
|
|
44
44
|
export { usePlanById } from "./modules/subscriptions/hooks/use-plan-by-id.hook";
|
|
45
45
|
export { useHasPlanAddon } from "./modules/subscriptions/hooks/use-has-plan-addon.hook";
|
|
46
|
+
export { useCalculateSubscription } from "./modules/subscriptions/hooks/calculate-subscription.hook";
|
|
47
|
+
export { useUpdateSubscriptionPlan } from "./modules/subscriptions/hooks/update-subscription-plan.hook";
|
|
48
|
+
export { useCards, CARDS_QUERY_KEY } from "./modules/cards/hooks/cards.hook";
|
|
46
49
|
export { useIaCredits } from "./modules/ia-credits/hooks/ia-credits.hook";
|
|
47
50
|
export type {
|
|
48
51
|
IaCreditsSummary,
|
|
@@ -57,10 +60,26 @@ export {
|
|
|
57
60
|
SubscriptionSchema,
|
|
58
61
|
SubscriptionItemSchema,
|
|
59
62
|
} from "./modules/subscriptions/types";
|
|
60
|
-
export { ADDON_IDS } from "./modules/subscriptions/constants/addons.constants";
|
|
61
|
-
export type { AddonKindEnum } from "./modules/subscriptions/constants/addons.constants";
|
|
63
|
+
export { ADDON_IDS, AI_CREDIT_OPTIONS, AI_CREDIT_VALUES } from "./modules/subscriptions/constants/addons.constants";
|
|
64
|
+
export type { AddonKindEnum, AiCreditOption } from "./modules/subscriptions/constants/addons.constants";
|
|
62
65
|
export type { Plan, PlanItem } from "./modules/subscriptions/types/plan.type";
|
|
63
66
|
export { PlanSchema, PlanItemSchema } from "./modules/subscriptions/types/plan.type";
|
|
67
|
+
export type { BillingPeriod } from "./modules/subscriptions/types/billing-period.type";
|
|
68
|
+
export type {
|
|
69
|
+
CalculateSubscriptionRequest,
|
|
70
|
+
CalculateSubscriptionResponse,
|
|
71
|
+
UpdateSubscriptionPlanRequest,
|
|
72
|
+
} from "./modules/subscriptions/types/calculate-subscription.type";
|
|
73
|
+
export type { Card } from "./modules/cards/types";
|
|
74
|
+
export { CardSchema } from "./modules/cards/types";
|
|
75
|
+
export { buildPlanExtras } from "./modules/subscriptions/utils/build-plan-extras";
|
|
76
|
+
export { getPriceFromCalculatedData, periodicityToBillingPeriod } from "./modules/subscriptions/utils/periodicity";
|
|
77
|
+
export { useBuyCreditsModal } from "./store/useBuyCreditsModal";
|
|
78
|
+
export { default as BuyCreditsModal } from "./components/modals/BuyCreditsModal";
|
|
79
|
+
export { Skeleton } from "./components/ui/feedback/Skeleton";
|
|
80
|
+
export { PaymentInfoCard } from "./components/ui/data-display/PaymentInfoCard";
|
|
81
|
+
export { CardBrandIcon } from "./components/ui/data-display/CardBrandIcons";
|
|
82
|
+
export { CardItem } from "./components/ui/data-display/CardItem";
|
|
64
83
|
|
|
65
84
|
// Providers
|
|
66
85
|
export { QueryProvider } from "./providers/query.provider";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useQuery } from '@tanstack/react-query';
|
|
4
|
+
import { withAction } from '../../../utils/withAction';
|
|
5
|
+
import { listCardsAction } from '../actions/list-cards.action';
|
|
6
|
+
|
|
7
|
+
export const CARDS_QUERY_KEY = ['cards'];
|
|
8
|
+
|
|
9
|
+
export function useCards() {
|
|
10
|
+
return useQuery({
|
|
11
|
+
queryKey: CARDS_QUERY_KEY,
|
|
12
|
+
queryFn: withAction(listCardsAction),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { api } from '../../../infra/api/client';
|
|
4
|
+
import { ApiError, ApiPaginatedActionResult, PaginatedSuccessResult } from '../../../infra/api/types';
|
|
5
|
+
import { buildQueryParams } from '../../../infra/utils/params';
|
|
6
|
+
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
7
|
+
import { Card, CardSchema } from '../types';
|
|
8
|
+
|
|
9
|
+
class CardsService {
|
|
10
|
+
async listCards(): Promise<PaginatedSuccessResult<Card>> {
|
|
11
|
+
const { id_account } = await getUserContext();
|
|
12
|
+
|
|
13
|
+
const query = buildQueryParams({ id_account });
|
|
14
|
+
const response = await api.apps.get<ApiPaginatedActionResult<Card>>(
|
|
15
|
+
`/accounts/${id_account}/cards?${query}`
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (response.status === 0) {
|
|
19
|
+
throw new ApiError(
|
|
20
|
+
response.message || 'Erro ao listar cartões',
|
|
21
|
+
'LIST_CARDS_FAILED',
|
|
22
|
+
400
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const data = response.data.map((item) => CardSchema.parse(item));
|
|
27
|
+
|
|
28
|
+
return { data, total: response.total, success: true };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const cardsService = new CardsService();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
|
|
3
|
+
export const CardSchema = z.object({
|
|
4
|
+
id: z.union([z.number(), z.string()]),
|
|
5
|
+
id_wl: z.number().optional(),
|
|
6
|
+
id_account: z.union([z.number(), z.string()]).optional(),
|
|
7
|
+
id_gateway: z.union([z.number(), z.string()]).optional(),
|
|
8
|
+
gateway_account: z.string().optional(),
|
|
9
|
+
gateway_card: z.string().optional(),
|
|
10
|
+
brand: z.string(),
|
|
11
|
+
name: z.string(),
|
|
12
|
+
number: z.string(),
|
|
13
|
+
date: z.string().optional(),
|
|
14
|
+
deleted: z
|
|
15
|
+
.number()
|
|
16
|
+
.transform((val) => val === 1)
|
|
17
|
+
.optional()
|
|
18
|
+
.default(false),
|
|
19
|
+
is_default: z.boolean().optional().default(false),
|
|
20
|
+
datetime_add: z.union([z.date(), z.coerce.date()]).nullable().optional(),
|
|
21
|
+
datetime_alt: z.union([z.date(), z.coerce.date()]).nullable().optional(),
|
|
22
|
+
datetime_del: z.union([z.date(), z.coerce.date()]).nullable().optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export type Card = z.infer<typeof CardSchema>;
|
|
26
|
+
|
|
27
|
+
export const CreateCardRequestSchema = z.object({
|
|
28
|
+
payment_method_id: z.string().min(1, 'payment_method_id é obrigatório'),
|
|
29
|
+
set_default: z.boolean().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type CreateCardRequest = z.infer<typeof CreateCardRequestSchema>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { safeServerAction } from '../../../utils/safeServerAction';
|
|
4
|
+
import type { CalculateSubscriptionRequest } from '../types/calculate-subscription.type';
|
|
5
|
+
import type { SubscriptionItem } from '../types';
|
|
6
|
+
import { subscriptionsService } from '../services/subscriptions.service';
|
|
7
|
+
import { resolvePlanExtrasPrices } from '../utils/resolve-plan-extras-prices';
|
|
8
|
+
|
|
9
|
+
export async function calculateSubscriptionAction(data: CalculateSubscriptionRequest) {
|
|
10
|
+
return safeServerAction(async () => {
|
|
11
|
+
if (data.plan_extras?.length) {
|
|
12
|
+
let subscriptionItems: SubscriptionItem[] = [];
|
|
13
|
+
|
|
14
|
+
if (data.id_subscription) {
|
|
15
|
+
const { data: subscriptions } = await subscriptionsService.listSubscriptions({
|
|
16
|
+
active: true,
|
|
17
|
+
});
|
|
18
|
+
const subscription =
|
|
19
|
+
subscriptions.find((s) => String(s.id) === String(data.id_subscription)) ??
|
|
20
|
+
subscriptions[0];
|
|
21
|
+
subscriptionItems = subscription?.items ?? [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
data = {
|
|
25
|
+
...data,
|
|
26
|
+
plan_extras: resolvePlanExtrasPrices(data.plan_extras, subscriptionItems),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return subscriptionsService.calculateSubscription(data);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { safeServerAction } from '../../../utils/safeServerAction';
|
|
4
|
+
import type { UpdateSubscriptionPlanRequest } from '../types/calculate-subscription.type';
|
|
5
|
+
import { subscriptionsService } from '../services/subscriptions.service';
|
|
6
|
+
import { resolvePlanExtrasPrices } from '../utils/resolve-plan-extras-prices';
|
|
7
|
+
|
|
8
|
+
export async function updateSubscriptionPlanAction(
|
|
9
|
+
subscriptionId: number | string,
|
|
10
|
+
data: UpdateSubscriptionPlanRequest
|
|
11
|
+
) {
|
|
12
|
+
return safeServerAction(async () => {
|
|
13
|
+
if (data.plan_extras?.length) {
|
|
14
|
+
const { data: subscriptions } = await subscriptionsService.listSubscriptions({ active: true });
|
|
15
|
+
const subscription =
|
|
16
|
+
subscriptions.find((s) => String(s.id) === String(subscriptionId)) ?? subscriptions[0];
|
|
17
|
+
|
|
18
|
+
data = {
|
|
19
|
+
...data,
|
|
20
|
+
plan_extras: resolvePlanExtrasPrices(data.plan_extras, subscription?.items ?? []),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return subscriptionsService.updateSubscriptionPlan(subscriptionId, data);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -8,3 +8,21 @@ export const ADDON_IDS = {
|
|
|
8
8
|
} as const;
|
|
9
9
|
|
|
10
10
|
export type AddonKindEnum = (typeof ADDON_IDS)[keyof typeof ADDON_IDS];
|
|
11
|
+
|
|
12
|
+
export interface AiCreditOption {
|
|
13
|
+
credits: number;
|
|
14
|
+
price: number;
|
|
15
|
+
priceLabel: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const AI_CREDIT_OPTIONS: AiCreditOption[] = [
|
|
19
|
+
{ credits: 0, price: 0, priceLabel: 'R$ 0,00' },
|
|
20
|
+
{ credits: 250, price: 9.99, priceLabel: 'R$ 9,99' },
|
|
21
|
+
{ credits: 500, price: 19.98, priceLabel: 'R$ 19,98' },
|
|
22
|
+
{ credits: 1000, price: 39.96, priceLabel: 'R$ 39,96' },
|
|
23
|
+
{ credits: 2500, price: 99.90, priceLabel: 'R$ 99,90' },
|
|
24
|
+
{ credits: 5000, price: 199.80, priceLabel: 'R$ 199,80' },
|
|
25
|
+
{ credits: 10000, price: 399.60, priceLabel: 'R$ 399,60' },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const AI_CREDIT_VALUES = new Set(AI_CREDIT_OPTIONS.map((opt) => opt.credits));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMutation } from '@tanstack/react-query';
|
|
4
|
+
import { withAction } from '../../../utils/withAction';
|
|
5
|
+
import { calculateSubscriptionAction } from '../actions/calculate-subscription.action';
|
|
6
|
+
import type { CalculateSubscriptionRequest } from '../types/calculate-subscription.type';
|
|
7
|
+
|
|
8
|
+
export function useCalculateSubscription() {
|
|
9
|
+
return useMutation({
|
|
10
|
+
mutationFn: (data: CalculateSubscriptionRequest) =>
|
|
11
|
+
withAction(calculateSubscriptionAction)(data),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
4
|
+
import { withAction } from '../../../utils/withAction';
|
|
5
|
+
import { updateSubscriptionPlanAction } from '../actions/update-subscription-plan.action';
|
|
6
|
+
import { SUBSCRIPTIONS_QUERY_KEY } from './list-subscriptions.hook';
|
|
7
|
+
import type { UpdateSubscriptionPlanRequest } from '../types/calculate-subscription.type';
|
|
8
|
+
|
|
9
|
+
export function useUpdateSubscriptionPlan() {
|
|
10
|
+
const queryClient = useQueryClient();
|
|
11
|
+
|
|
12
|
+
return useMutation({
|
|
13
|
+
mutationFn: ({
|
|
14
|
+
subscriptionId,
|
|
15
|
+
data,
|
|
16
|
+
}: {
|
|
17
|
+
subscriptionId: number | string;
|
|
18
|
+
data: UpdateSubscriptionPlanRequest;
|
|
19
|
+
}) => withAction(updateSubscriptionPlanAction)(subscriptionId, data),
|
|
20
|
+
onSuccess: () => {
|
|
21
|
+
queryClient.invalidateQueries({ queryKey: SUBSCRIPTIONS_QUERY_KEY });
|
|
22
|
+
queryClient.invalidateQueries({ queryKey: ['charges'] });
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import 'server-only';
|
|
2
2
|
|
|
3
3
|
import { api } from '../../../infra/api/client';
|
|
4
|
-
import { ApiError, ApiPaginatedActionResult, PaginatedSuccessResult } from '../../../infra/api/types';
|
|
4
|
+
import { ApiError, ApiActionResult, ApiPaginatedActionResult, PaginatedSuccessResult, SuccessResult } from '../../../infra/api/types';
|
|
5
5
|
import { buildQueryParams } from '../../../infra/utils/params';
|
|
6
6
|
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
7
7
|
import { Subscription, SubscriptionSchema, FindSubscriptionsParams } from '../types';
|
|
8
|
+
import {
|
|
9
|
+
type CalculateSubscriptionRequest,
|
|
10
|
+
type CalculateSubscriptionResponse,
|
|
11
|
+
type UpdateSubscriptionPlanRequest,
|
|
12
|
+
CalculateSubscriptionResponseSchema,
|
|
13
|
+
} from '../types/calculate-subscription.type';
|
|
8
14
|
|
|
9
15
|
class SubscriptionsService {
|
|
10
16
|
async listSubscriptions(
|
|
@@ -33,6 +39,51 @@ class SubscriptionsService {
|
|
|
33
39
|
success: true,
|
|
34
40
|
} satisfies PaginatedSuccessResult<Subscription>;
|
|
35
41
|
}
|
|
42
|
+
async calculateSubscription(
|
|
43
|
+
data: CalculateSubscriptionRequest
|
|
44
|
+
): Promise<SuccessResult<CalculateSubscriptionResponse>> {
|
|
45
|
+
const { id_account } = await getUserContext();
|
|
46
|
+
|
|
47
|
+
const response = await api.apps.post<ApiActionResult<CalculateSubscriptionResponse>>(
|
|
48
|
+
`/accounts/${id_account}/subscriptions/action/calculate`,
|
|
49
|
+
data
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (response.status === 0) {
|
|
53
|
+
throw new ApiError(
|
|
54
|
+
response.message || 'Erro ao calcular assinatura',
|
|
55
|
+
'CALCULATE_SUBSCRIPTION_FAILED',
|
|
56
|
+
400
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
data: CalculateSubscriptionResponseSchema.parse(response.data),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async updateSubscriptionPlan(
|
|
67
|
+
subscriptionId: number | string,
|
|
68
|
+
data: UpdateSubscriptionPlanRequest
|
|
69
|
+
): Promise<SuccessResult<void>> {
|
|
70
|
+
const { id_account } = await getUserContext();
|
|
71
|
+
|
|
72
|
+
const response = await api.apps.put<ApiActionResult<void>>(
|
|
73
|
+
`/accounts/${id_account}/subscriptions/${subscriptionId}/action/plan`,
|
|
74
|
+
data
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (response.status === 0) {
|
|
78
|
+
throw new ApiError(
|
|
79
|
+
response.message || 'Erro ao atualizar plano',
|
|
80
|
+
'UPDATE_SUBSCRIPTION_PLAN_FAILED',
|
|
81
|
+
400
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { success: true };
|
|
86
|
+
}
|
|
36
87
|
}
|
|
37
88
|
|
|
38
89
|
export const subscriptionsService = new SubscriptionsService();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type BillingPeriod = 'monthly' | 'semiannual' | 'annual';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
|
|
3
|
+
export const CalculateSubscriptionPlanExtraRequestSchema = z.object({
|
|
4
|
+
id_addon: z.number(),
|
|
5
|
+
value: z.number(),
|
|
6
|
+
quantity: z.number(),
|
|
7
|
+
currency: z.string(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const CalculateSubscriptionRequestSchema = z.object({
|
|
11
|
+
id_product: z.number(),
|
|
12
|
+
id_plan: z.number().optional(),
|
|
13
|
+
id_subscription: z.union([z.number(), z.string()]).optional(),
|
|
14
|
+
id_coupon: z.number().optional(),
|
|
15
|
+
plan_extras: z.array(CalculateSubscriptionPlanExtraRequestSchema.partial({ value: true })).optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type CalculateSubscriptionRequest = z.infer<typeof CalculateSubscriptionRequestSchema>;
|
|
19
|
+
|
|
20
|
+
export const CalculateSubscriptionPlanExtraResponseSchema = z.object({
|
|
21
|
+
id_addon: z.number(),
|
|
22
|
+
value: z.number(),
|
|
23
|
+
value_original: z.number().optional().default(0),
|
|
24
|
+
value_discount: z.number().optional().default(0),
|
|
25
|
+
quantity: z.number(),
|
|
26
|
+
currency: z.string(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const CalculateSubscriptionResponseSchema = z.object({
|
|
30
|
+
name: z.string(),
|
|
31
|
+
month: z.number(),
|
|
32
|
+
month_discount: z.number(),
|
|
33
|
+
month_discount_coupom: z.number().optional().default(0),
|
|
34
|
+
month_original: z.number(),
|
|
35
|
+
month_original_discount: z.number().optional().default(0),
|
|
36
|
+
month_original_discount_cupom: z.number().optional().default(0),
|
|
37
|
+
semmester: z.number(),
|
|
38
|
+
semmester_price: z.number().optional().default(0),
|
|
39
|
+
semmester_discount: z.number(),
|
|
40
|
+
semmester_discount_coupom: z.number().optional().default(0),
|
|
41
|
+
semmester_original: z.number(),
|
|
42
|
+
semmester_original_discount: z.number().optional().default(0),
|
|
43
|
+
semmester_original_discount_cupom: z.number().optional().default(0),
|
|
44
|
+
year: z.number(),
|
|
45
|
+
year_discount: z.number(),
|
|
46
|
+
year_discount_coupom: z.number().optional().default(0),
|
|
47
|
+
year_original: z.number(),
|
|
48
|
+
year_original_discount: z.number().optional().default(0),
|
|
49
|
+
year_original_discount_cupom: z.number().optional().default(0),
|
|
50
|
+
plan_extras: z.array(CalculateSubscriptionPlanExtraResponseSchema),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type CalculateSubscriptionResponse = z.infer<typeof CalculateSubscriptionResponseSchema>;
|
|
54
|
+
|
|
55
|
+
export const UpdateSubscriptionPlanExtraSchema = z.object({
|
|
56
|
+
id_addon: z.number(),
|
|
57
|
+
value: z.number().optional(),
|
|
58
|
+
quantity: z.number(),
|
|
59
|
+
price: z.string().optional(),
|
|
60
|
+
trial: z.boolean().optional(),
|
|
61
|
+
trial_days: z.number().optional(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export const UpdateSubscriptionPlanRequestSchema = z.object({
|
|
65
|
+
id_plan: z.number().optional(),
|
|
66
|
+
id_coupon: z.union([z.number(), z.null()]).optional(),
|
|
67
|
+
date_due: z.string().optional(),
|
|
68
|
+
type: z.string().optional(),
|
|
69
|
+
active: z.boolean().optional(),
|
|
70
|
+
periodicity: z.number().optional(),
|
|
71
|
+
plan_extras: z.array(UpdateSubscriptionPlanExtraSchema).optional(),
|
|
72
|
+
enable_charge: z.boolean().optional(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export type UpdateSubscriptionPlanRequest = z.infer<typeof UpdateSubscriptionPlanRequestSchema>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { SubscriptionItem } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface PlanExtraOverride {
|
|
4
|
+
id_addon: number;
|
|
5
|
+
quantity: number;
|
|
6
|
+
currency: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PlanExtra {
|
|
10
|
+
id_addon: number;
|
|
11
|
+
value: number;
|
|
12
|
+
quantity: number;
|
|
13
|
+
currency: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildPlanExtras(
|
|
17
|
+
items: SubscriptionItem[],
|
|
18
|
+
overrides: PlanExtraOverride[]
|
|
19
|
+
): PlanExtra[] {
|
|
20
|
+
const overrideMap = new Map(overrides.map((o) => [o.id_addon, o]));
|
|
21
|
+
|
|
22
|
+
const extras: PlanExtra[] = items
|
|
23
|
+
.filter((item) => item.id_addon != null)
|
|
24
|
+
.map((item) => {
|
|
25
|
+
const override = overrideMap.get(item.id_addon!);
|
|
26
|
+
if (override) {
|
|
27
|
+
overrideMap.delete(item.id_addon!);
|
|
28
|
+
return {
|
|
29
|
+
id_addon: override.id_addon,
|
|
30
|
+
value: 0,
|
|
31
|
+
quantity: override.quantity,
|
|
32
|
+
currency: override.currency,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
id_addon: item.id_addon!,
|
|
37
|
+
value: item.value,
|
|
38
|
+
quantity: item.quantity,
|
|
39
|
+
currency: item.currency,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
for (const override of overrideMap.values()) {
|
|
44
|
+
extras.push({
|
|
45
|
+
id_addon: override.id_addon,
|
|
46
|
+
value: 0,
|
|
47
|
+
quantity: override.quantity,
|
|
48
|
+
currency: override.currency,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return extras;
|
|
53
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { BillingPeriod } from '../types/billing-period.type';
|
|
2
|
+
import type { CalculateSubscriptionResponse } from '../types/calculate-subscription.type';
|
|
3
|
+
|
|
4
|
+
export function periodicityToBillingPeriod(
|
|
5
|
+
periodicity: number | string | null | undefined
|
|
6
|
+
): BillingPeriod | null {
|
|
7
|
+
if (periodicity === 30 || periodicity === 1 || periodicity === 'monthly') return 'monthly';
|
|
8
|
+
if (periodicity === 180 || periodicity === 3 || periodicity === 'semiannual') return 'semiannual';
|
|
9
|
+
if (periodicity === 365 || periodicity === 2 || periodicity === 'annual') return 'annual';
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function billingPeriodToNumeric(billingPeriod: BillingPeriod): 30 | 180 | 365 {
|
|
14
|
+
if (billingPeriod === 'semiannual') return 180;
|
|
15
|
+
if (billingPeriod === 'annual') return 365;
|
|
16
|
+
return 30;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getPeriodicityLabel(periodicity: number | string | null | undefined): string {
|
|
20
|
+
const period = periodicityToBillingPeriod(periodicity);
|
|
21
|
+
if (period === 'semiannual') return 'Semestral';
|
|
22
|
+
if (period === 'annual') return 'Anual';
|
|
23
|
+
return 'Mensal';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getBillingPeriodSuffix(billingPeriod: BillingPeriod): string {
|
|
27
|
+
if (billingPeriod === 'semiannual') return '/semestre';
|
|
28
|
+
if (billingPeriod === 'annual') return '/ano';
|
|
29
|
+
return '/mês';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getBillingPeriodLabel(billingPeriod: BillingPeriod): string {
|
|
33
|
+
if (billingPeriod === 'semiannual') return 'semestral';
|
|
34
|
+
if (billingPeriod === 'annual') return 'anual';
|
|
35
|
+
return 'mensal';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getBillingPeriodMonths(billingPeriod: BillingPeriod): number {
|
|
39
|
+
if (billingPeriod === 'semiannual') return 6;
|
|
40
|
+
if (billingPeriod === 'annual') return 12;
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getPriceFromCalculatedData(
|
|
45
|
+
data: CalculateSubscriptionResponse,
|
|
46
|
+
periodicity: number | string | null | undefined
|
|
47
|
+
): number {
|
|
48
|
+
const period = periodicityToBillingPeriod(periodicity);
|
|
49
|
+
if (period === 'annual') return data.year;
|
|
50
|
+
if (period === 'semiannual') return data.semmester;
|
|
51
|
+
return data.month;
|
|
52
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import type { SubscriptionItem } from '../types';
|
|
4
|
+
|
|
5
|
+
interface PlanExtraInput {
|
|
6
|
+
id_addon: number;
|
|
7
|
+
value?: number;
|
|
8
|
+
quantity: number;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resolvePlanExtrasPrices<T extends PlanExtraInput>(
|
|
13
|
+
extras: T[],
|
|
14
|
+
subscriptionItems: SubscriptionItem[]
|
|
15
|
+
): T[] {
|
|
16
|
+
return extras.map((extra) => {
|
|
17
|
+
const item = subscriptionItems.find((i) => i.id_addon === extra.id_addon);
|
|
18
|
+
if (item) {
|
|
19
|
+
return { ...extra, value: item.value };
|
|
20
|
+
}
|
|
21
|
+
return { ...extra, value: extra.value ?? 0 };
|
|
22
|
+
});
|
|
23
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { whitelabelStylesService } from './modules/whitelabel/services/whitelabe
|
|
|
10
10
|
export { findWhitelabelStyles } from './modules/whitelabel/actions/find-whitelabel-styles.action';
|
|
11
11
|
export { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';
|
|
12
12
|
export { plansService } from './modules/subscriptions/services/plans.service';
|
|
13
|
+
export { cardsService } from './modules/cards/services/cards.service';
|
|
13
14
|
export { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';
|
|
14
15
|
|
|
15
16
|
// Actions
|
|
@@ -19,6 +20,10 @@ export { findUserById } from './modules/users/action/find-user-by-id.action';
|
|
|
19
20
|
export { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';
|
|
20
21
|
export { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';
|
|
21
22
|
export { findPlanByIdAction } from './modules/subscriptions/actions/find-plan-by-id.action';
|
|
23
|
+
export { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';
|
|
24
|
+
export { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';
|
|
25
|
+
export { listCardsAction } from './modules/cards/actions/list-cards.action';
|
|
26
|
+
export { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';
|
|
22
27
|
export { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';
|
|
23
28
|
|
|
24
29
|
// Project Users Services & Actions (server-only)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useModalManager } from './useModalManager';
|
|
2
|
+
|
|
3
|
+
interface BuyCreditsModalData {
|
|
4
|
+
onConfirm?: (creditAmount: number, price: number) => void;
|
|
5
|
+
initialActiveCardId?: string;
|
|
6
|
+
initialSelectedValue?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const useBuyCreditsModal = () => {
|
|
10
|
+
const { activeModal, modalData, openModal, closeModal } = useModalManager();
|
|
11
|
+
|
|
12
|
+
const isOpen = activeModal === 'buyCreditsModal';
|
|
13
|
+
const data = modalData as BuyCreditsModalData;
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
open: isOpen,
|
|
17
|
+
onConfirm: data?.onConfirm,
|
|
18
|
+
initialActiveCardId: data?.initialActiveCardId,
|
|
19
|
+
initialSelectedValue: data?.initialSelectedValue,
|
|
20
|
+
|
|
21
|
+
openModal: (onConfirm?: (creditAmount: number, price: number) => void) =>
|
|
22
|
+
openModal('buyCreditsModal', { onConfirm }),
|
|
23
|
+
closeModal,
|
|
24
|
+
};
|
|
25
|
+
};
|