@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.
Files changed (73) hide show
  1. package/dist/components/modals/BuyCreditsModal.mjs +277 -0
  2. package/dist/components/modals/BuyCreditsModal.mjs.map +1 -0
  3. package/dist/components/navigation/subcomponents/CreditsCard.mjs +5 -2
  4. package/dist/components/navigation/subcomponents/CreditsCard.mjs.map +1 -1
  5. package/dist/components/ui/data-display/CardBrandIcons.mjs +484 -0
  6. package/dist/components/ui/data-display/CardBrandIcons.mjs.map +1 -0
  7. package/dist/components/ui/data-display/CardItem.mjs +67 -0
  8. package/dist/components/ui/data-display/CardItem.mjs.map +1 -0
  9. package/dist/components/ui/data-display/PaymentInfoCard.mjs +161 -0
  10. package/dist/components/ui/data-display/PaymentInfoCard.mjs.map +1 -0
  11. package/dist/components/ui/feedback/Skeleton.mjs +9 -0
  12. package/dist/components/ui/feedback/Skeleton.mjs.map +1 -0
  13. package/dist/index.mjs +57 -29
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/modules/cards/actions/list-cards.action.mjs +10 -0
  16. package/dist/modules/cards/actions/list-cards.action.mjs.map +1 -0
  17. package/dist/modules/cards/hooks/cards.hook.mjs +16 -0
  18. package/dist/modules/cards/hooks/cards.hook.mjs.map +1 -0
  19. package/dist/modules/cards/services/cards.service.mjs +29 -0
  20. package/dist/modules/cards/services/cards.service.mjs.map +1 -0
  21. package/dist/modules/cards/types.mjs +27 -0
  22. package/dist/modules/cards/types.mjs.map +1 -0
  23. package/dist/modules/subscriptions/actions/calculate-subscription.action.mjs +27 -0
  24. package/dist/modules/subscriptions/actions/calculate-subscription.action.mjs.map +1 -0
  25. package/dist/modules/subscriptions/actions/update-subscription-plan.action.mjs +21 -0
  26. package/dist/modules/subscriptions/actions/update-subscription-plan.action.mjs.map +1 -0
  27. package/dist/modules/subscriptions/constants/addons.constants.mjs +13 -1
  28. package/dist/modules/subscriptions/constants/addons.constants.mjs.map +1 -1
  29. package/dist/modules/subscriptions/hooks/calculate-subscription.hook.mjs +13 -0
  30. package/dist/modules/subscriptions/hooks/calculate-subscription.hook.mjs.map +1 -0
  31. package/dist/modules/subscriptions/hooks/update-subscription-plan.hook.mjs +22 -0
  32. package/dist/modules/subscriptions/hooks/update-subscription-plan.hook.mjs.map +1 -0
  33. package/dist/modules/subscriptions/services/subscriptions.service.mjs +36 -0
  34. package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -1
  35. package/dist/modules/subscriptions/types/billing-period.type.mjs +1 -0
  36. package/dist/modules/subscriptions/types/billing-period.type.mjs.map +1 -0
  37. package/dist/modules/subscriptions/types/calculate-subscription.type.mjs +72 -0
  38. package/dist/modules/subscriptions/types/calculate-subscription.type.mjs.map +1 -0
  39. package/dist/modules/subscriptions/utils/build-plan-extras.mjs +34 -0
  40. package/dist/modules/subscriptions/utils/build-plan-extras.mjs.map +1 -0
  41. package/dist/modules/subscriptions/utils/periodicity.mjs +48 -0
  42. package/dist/modules/subscriptions/utils/periodicity.mjs.map +1 -0
  43. package/dist/modules/subscriptions/utils/resolve-plan-extras-prices.mjs +14 -0
  44. package/dist/modules/subscriptions/utils/resolve-plan-extras-prices.mjs.map +1 -0
  45. package/dist/server.mjs +10 -0
  46. package/dist/server.mjs.map +1 -1
  47. package/dist/store/useBuyCreditsModal.mjs +18 -0
  48. package/dist/store/useBuyCreditsModal.mjs.map +1 -0
  49. package/package.json +1 -1
  50. package/src/components/modals/BuyCreditsModal.tsx +304 -0
  51. package/src/components/navigation/subcomponents/CreditsCard.tsx +5 -2
  52. package/src/components/ui/data-display/CardBrandIcons.tsx +336 -0
  53. package/src/components/ui/data-display/CardItem.tsx +75 -0
  54. package/src/components/ui/data-display/PaymentInfoCard.tsx +186 -0
  55. package/src/components/ui/feedback/Skeleton.tsx +5 -0
  56. package/src/index.ts +21 -2
  57. package/src/modules/cards/actions/list-cards.action.ts +8 -0
  58. package/src/modules/cards/hooks/cards.hook.ts +14 -0
  59. package/src/modules/cards/services/cards.service.ts +32 -0
  60. package/src/modules/cards/types.ts +32 -0
  61. package/src/modules/subscriptions/actions/calculate-subscription.action.ts +32 -0
  62. package/src/modules/subscriptions/actions/update-subscription-plan.action.ts +26 -0
  63. package/src/modules/subscriptions/constants/addons.constants.ts +18 -0
  64. package/src/modules/subscriptions/hooks/calculate-subscription.hook.ts +13 -0
  65. package/src/modules/subscriptions/hooks/update-subscription-plan.hook.ts +25 -0
  66. package/src/modules/subscriptions/services/subscriptions.service.ts +52 -1
  67. package/src/modules/subscriptions/types/billing-period.type.ts +1 -0
  68. package/src/modules/subscriptions/types/calculate-subscription.type.ts +75 -0
  69. package/src/modules/subscriptions/utils/build-plan-extras.ts +53 -0
  70. package/src/modules/subscriptions/utils/periodicity.ts +52 -0
  71. package/src/modules/subscriptions/utils/resolve-plan-extras-prices.ts +23 -0
  72. package/src/server.ts +5 -0
  73. 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,8 @@
1
+ 'use server';
2
+
3
+ import { safeServerAction } from '../../../utils/safeServerAction';
4
+ import { cardsService } from '../services/cards.service';
5
+
6
+ export async function listCardsAction() {
7
+ return safeServerAction(() => cardsService.listCards());
8
+ }
@@ -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
+ };