@greatapps/common 1.1.565 → 1.1.567

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 (37) hide show
  1. package/dist/components/modals/cards/CannotDeleteCardModal.mjs +100 -0
  2. package/dist/components/modals/cards/CannotDeleteCardModal.mjs.map +1 -0
  3. package/dist/components/modals/cards/DeleteCardModal.mjs +121 -0
  4. package/dist/components/modals/cards/DeleteCardModal.mjs.map +1 -0
  5. package/dist/components/ui/data-display/CardItem.mjs +68 -17
  6. package/dist/components/ui/data-display/CardItem.mjs.map +1 -1
  7. package/dist/components/ui/data-display/PaymentInfoCard.mjs +22 -26
  8. package/dist/components/ui/data-display/PaymentInfoCard.mjs.map +1 -1
  9. package/dist/index.mjs +10 -0
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/modules/cards/hooks/delete-confirmation.hook.mjs +31 -0
  12. package/dist/modules/cards/hooks/delete-confirmation.hook.mjs.map +1 -0
  13. package/dist/modules/cards/hooks/handle-delete-card.hook.mjs +31 -0
  14. package/dist/modules/cards/hooks/handle-delete-card.hook.mjs.map +1 -0
  15. package/dist/modules/subscriptions/actions/update-subscription-payment.action.mjs +12 -0
  16. package/dist/modules/subscriptions/actions/update-subscription-payment.action.mjs.map +1 -0
  17. package/dist/modules/subscriptions/hooks/update-subscription-payment.hook.mjs +23 -0
  18. package/dist/modules/subscriptions/hooks/update-subscription-payment.hook.mjs.map +1 -0
  19. package/dist/modules/subscriptions/services/subscriptions.service.mjs +16 -0
  20. package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -1
  21. package/dist/modules/subscriptions/types/subscription.type.mjs +1 -1
  22. package/dist/modules/subscriptions/types/subscription.type.mjs.map +1 -1
  23. package/dist/server.mjs +2 -0
  24. package/dist/server.mjs.map +1 -1
  25. package/package.json +1 -1
  26. package/src/components/modals/cards/CannotDeleteCardModal.tsx +123 -0
  27. package/src/components/modals/cards/DeleteCardModal.tsx +132 -0
  28. package/src/components/ui/data-display/CardItem.tsx +70 -12
  29. package/src/components/ui/data-display/PaymentInfoCard.tsx +28 -28
  30. package/src/index.ts +6 -0
  31. package/src/modules/cards/hooks/delete-confirmation.hook.ts +42 -0
  32. package/src/modules/cards/hooks/handle-delete-card.hook.ts +42 -0
  33. package/src/modules/subscriptions/actions/update-subscription-payment.action.ts +13 -0
  34. package/src/modules/subscriptions/hooks/update-subscription-payment.hook.ts +25 -0
  35. package/src/modules/subscriptions/services/subscriptions.service.ts +23 -0
  36. package/src/modules/subscriptions/types/subscription.type.ts +1 -1
  37. package/src/server.ts +1 -0
@@ -0,0 +1,132 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Dialog,
5
+ DialogContent,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ DialogDescription,
9
+ } from '../../ui/overlay/Dialog';
10
+ import { Button } from '../../ui/buttons/Button';
11
+ import { Separator } from '../../ui/data-display/Separator';
12
+ import { Toast } from '../../ui/feedback/Toast';
13
+ import { FormField } from '../../ui/form/FormField';
14
+ import { IconTrash } from '@tabler/icons-react';
15
+ import { toast } from 'sonner';
16
+ import { useModalManager } from '../../../store/useModalManager';
17
+ import { useDeleteConfirmation } from '../../../modules/cards/hooks/delete-confirmation.hook';
18
+ import { useDeleteCard } from '../../../modules/cards/hooks/delete-card.hook';
19
+ import { useCards } from '../../../modules/cards/hooks/cards.hook';
20
+
21
+ export function DeleteCardModal() {
22
+ const { activeModal, modalData, closeModal } = useModalManager();
23
+ const { form, isValidConfirmation, resetConfirmation } = useDeleteConfirmation();
24
+ const deleteCard = useDeleteCard();
25
+ const { data: cardsData } = useCards();
26
+
27
+ const isOpen = activeModal === 'deleteCardModal';
28
+ const cardId = modalData.cardId as string | undefined;
29
+ const onSuccess = modalData.onSuccess as (() => void) | undefined;
30
+
31
+ const card = cardsData?.data?.find((c) => c.id.toString() === cardId);
32
+ const cardLastDigits = (card?.number ?? '').replaceAll(/\D/g, '').slice(-4);
33
+
34
+ const handleClose = () => {
35
+ resetConfirmation();
36
+ closeModal();
37
+ };
38
+
39
+ const onSubmit = async () => {
40
+ if (!cardId) return;
41
+
42
+ try {
43
+ await deleteCard.mutateAsync(cardId);
44
+ } catch (error) {
45
+ toast.custom(
46
+ (t) => (
47
+ <Toast
48
+ toastId={t}
49
+ variant="error"
50
+ message={
51
+ error instanceof Error
52
+ ? error.message
53
+ : 'Não foi possível excluir o cartão'
54
+ }
55
+ />
56
+ ),
57
+ { duration: 5000 },
58
+ );
59
+ return;
60
+ }
61
+
62
+ onSuccess?.();
63
+ resetConfirmation();
64
+ closeModal();
65
+
66
+ toast.custom(
67
+ (t) => (
68
+ <Toast
69
+ variant="success"
70
+ message={`O cartão •••• ${cardLastDigits} foi excluído com sucesso`}
71
+ toastId={t}
72
+ />
73
+ ),
74
+ { duration: 5000 },
75
+ );
76
+ };
77
+
78
+ return (
79
+ <Dialog open={isOpen} onOpenChange={(open) => !open && handleClose()}>
80
+ <DialogContent className="flex flex-col p-0! gap-0! max-w-[calc(100%-2rem)]! w-full sm:max-w-[400px]! lg:max-w-[410px]! rounded-lg border">
81
+ <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col">
82
+ <DialogHeader className="gap-4 text-left p-4 lg:p-5 pb-0 lg:pb-0">
83
+ <div className="flex items-center justify-center w-fit bg-red-50 rounded-lg p-2.5">
84
+ <IconTrash size={20} className="text-red-500" />
85
+ </div>
86
+ <div className="flex flex-col gap-2">
87
+ <DialogTitle className="paragraph-medium-semibold text-zinc-950">
88
+ Excluir cartão
89
+ </DialogTitle>
90
+ <DialogDescription className="paragraph-small-regular text-zinc-600">
91
+ Deseja realmente excluir o cartão:{' '}
92
+ <span className="font-semibold text-zinc-950">
93
+ •••• {cardLastDigits}
94
+ </span>
95
+ ?
96
+ </DialogDescription>
97
+ </div>
98
+ </DialogHeader>
99
+
100
+ <div className="px-4 lg:px-5 py-5">
101
+ <FormField
102
+ label="Digite EXCLUIR abaixo"
103
+ required
104
+ placeholder="EXCLUIR"
105
+ {...form.register('confirmText')}
106
+ />
107
+ </div>
108
+ <Separator />
109
+ <div className="flex flex-row items-center gap-2 p-4 lg:p-5">
110
+ <Button
111
+ type="submit"
112
+ disabled={!isValidConfirmation}
113
+ loading={form.formState.isSubmitting}
114
+ className="h-10!"
115
+ >
116
+ {form.formState.isSubmitting ? 'Excluindo...' : 'Excluir'}
117
+ </Button>
118
+ <Button
119
+ type="button"
120
+ variant="secondary"
121
+ className="h-10!"
122
+ onClick={handleClose}
123
+ disabled={form.formState.isSubmitting}
124
+ >
125
+ Cancelar
126
+ </Button>
127
+ </div>
128
+ </form>
129
+ </DialogContent>
130
+ </Dialog>
131
+ );
132
+ }
@@ -1,10 +1,14 @@
1
1
  'use client';
2
2
 
3
3
  import { useState } from 'react';
4
- import { IconDotsVertical, IconTrash } from '@tabler/icons-react';
4
+ import { toast } from 'sonner';
5
+ import { IconDotsVertical, IconStar, IconTrash } from '@tabler/icons-react';
5
6
  import { Popover, PopoverContent, PopoverTrigger } from '../overlay/Popover';
6
7
  import { NavBarItem } from '../../layouts/NavBarItem';
8
+ import { Toast } from '../feedback/Toast';
7
9
  import { CardBrandIcon } from './CardBrandIcons';
10
+ import { useSetDefaultCard } from '../../../modules/cards/hooks/set-default-card.hook';
11
+ import { useHandleDeleteCard } from '../../../modules/cards/hooks/handle-delete-card.hook';
8
12
  import type { Card } from '../../../modules/cards/types';
9
13
 
10
14
  type CardItemProps = {
@@ -12,24 +16,63 @@ type CardItemProps = {
12
16
  isSelected?: boolean;
13
17
  onSelect?: () => void;
14
18
  showMenu?: boolean;
15
- canDelete?: boolean;
16
- onDelete?: () => void;
19
+ hideDelete?: boolean;
20
+ hideSetDefault?: boolean;
21
+ onAfterDelete?: (card: Card) => void;
22
+ onAfterSetDefault?: (card: Card) => void;
17
23
  };
18
24
 
19
25
  export function CardItem({
20
26
  card,
21
27
  isSelected,
22
28
  onSelect,
23
- showMenu = false,
24
- canDelete = true,
25
- onDelete,
29
+ showMenu = true,
30
+ hideDelete = false,
31
+ hideSetDefault = false,
32
+ onAfterDelete,
33
+ onAfterSetDefault,
26
34
  }: CardItemProps) {
27
35
  const [popoverOpen, setPopoverOpen] = useState(false);
28
36
 
37
+ const setDefaultCardMutation = useSetDefaultCard();
38
+
39
+ const handleDeleteCard = useHandleDeleteCard({ onAfterDelete });
40
+
41
+ const handleSetDefault = () => {
42
+ setDefaultCardMutation.mutate(card.id.toString(), {
43
+ onSuccess: () => {
44
+ toast.custom((t) => (
45
+ <Toast
46
+ variant="success"
47
+ message="Cartão definido como padrão"
48
+ toastId={t}
49
+ />
50
+ ));
51
+ onAfterSetDefault?.(card);
52
+ },
53
+ onError: () => {
54
+ toast.custom((t) => (
55
+ <Toast
56
+ variant="error"
57
+ message="Erro ao definir cartão padrão"
58
+ toastId={t}
59
+ />
60
+ ));
61
+ },
62
+ });
63
+ };
64
+
65
+ const menuHasSetDefault = !hideSetDefault && !card.is_default;
66
+ const menuHasDelete = !hideDelete;
67
+ const hasMenuItems = menuHasSetDefault || menuHasDelete;
68
+ const renderMenu = showMenu && hasMenuItems;
69
+
29
70
  return (
30
71
  <div
31
72
  className={`flex items-center p-4 cursor-pointer transition-colors gap-3 rounded-lg border ${
32
- isSelected ? 'bg-zinc-50 border-zinc-950' : 'bg-white border-zinc-200 hover:bg-zinc-50'
73
+ isSelected
74
+ ? 'bg-zinc-50 border-zinc-950'
75
+ : 'bg-white border-zinc-200 hover:bg-zinc-50'
33
76
  }`}
34
77
  onClick={onSelect}
35
78
  >
@@ -41,8 +84,13 @@ export function CardItem({
41
84
  •••• {(card.number ?? '').replaceAll(/\D/g, '').slice(-4)}
42
85
  </span>
43
86
  </div>
87
+ {card.is_default && (
88
+ <span className="paragraph-xsmall-semibold text-zinc-600 bg-zinc-100 px-2 py-0.5 rounded-lg whitespace-nowrap">
89
+ Padrão
90
+ </span>
91
+ )}
44
92
  <CardBrandIcon brand={card.brand} />
45
- {showMenu && (
93
+ {renderMenu && (
46
94
  <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
47
95
  <PopoverTrigger asChild>
48
96
  <button
@@ -54,16 +102,26 @@ export function CardItem({
54
102
  </PopoverTrigger>
55
103
  <PopoverContent
56
104
  align="end"
57
- className="w-[140px] p-2 bg-white rounded-2xl shadow-md border border-zinc-200"
105
+ className="w-[180px] p-2 bg-white rounded-2xl border border-zinc-200"
58
106
  onClick={(e) => e.stopPropagation()}
59
107
  >
60
- {canDelete && (
108
+ {menuHasSetDefault && (
109
+ <NavBarItem
110
+ icon={<IconStar size={18} className="text-zinc-400" />}
111
+ label="Tornar padrão"
112
+ onClick={() => {
113
+ setPopoverOpen(false);
114
+ handleSetDefault();
115
+ }}
116
+ />
117
+ )}
118
+ {menuHasDelete && (
61
119
  <NavBarItem
62
- icon={<IconTrash size={20} className="text-zinc-400" />}
120
+ icon={<IconTrash size={18} className="text-zinc-400" />}
63
121
  label="Excluir"
64
122
  onClick={() => {
65
123
  setPopoverOpen(false);
66
- onDelete?.();
124
+ handleDeleteCard(card);
67
125
  }}
68
126
  />
69
127
  )}
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
+ import { useCallback, useState } from 'react';
3
4
  import { IconChevronDown, IconPlus } from '@tabler/icons-react';
4
5
  import { CardBrandIcon } from './CardBrandIcons';
5
6
  import { CardItem } from './CardItem';
6
7
  import useIsMobile from '../../../hooks/useIsMobile';
7
8
  import { useCards } from '../../../modules/cards/hooks/cards.hook';
8
- import { useCallback, useState } from 'react';
9
9
  import { useModalManager } from '../../../store/useModalManager';
10
10
  import { useIsDefaultWhitelabel } from '../../../providers/whitelabel.provider';
11
11
  import type { Card } from '../../../modules/cards/types';
@@ -16,6 +16,8 @@ type PaymentInfoCardProps = {
16
16
  onCardSelect: (cardId: string) => void;
17
17
  onManageCards?: () => void;
18
18
  onOpenAddCard?: () => void;
19
+ hideDelete?: boolean;
20
+ hideSetDefault?: boolean;
19
21
  };
20
22
 
21
23
  export function PaymentInfoCard({
@@ -24,6 +26,8 @@ export function PaymentInfoCard({
24
26
  onCardSelect,
25
27
  onManageCards,
26
28
  onOpenAddCard,
29
+ hideDelete,
30
+ hideSetDefault,
27
31
  }: PaymentInfoCardProps) {
28
32
  const isMobile = useIsMobile();
29
33
  const { openModal } = useModalManager();
@@ -34,9 +38,11 @@ export function PaymentInfoCard({
34
38
  const { data: cardsData } = useCards();
35
39
  const cards = cardsData?.data ?? [];
36
40
 
37
- const selectedCard = cards.find((card) => card.id.toString() === selectedCardId.toString());
41
+ const selectedCard = cards.find(
42
+ (card) => card.id.toString() === selectedCardId.toString(),
43
+ );
38
44
 
39
- const canDeleteCards = cards.length > 1;
45
+ const onlyOneCard = cards.length <= 1;
40
46
  const hasCards = cards.length > 0;
41
47
 
42
48
  const hasMoreThan3Cards = cards.length > 3;
@@ -53,32 +59,24 @@ export function PaymentInfoCard({
53
59
  });
54
60
  }, [openModal, onOpenAddCard, onCardSelect]);
55
61
 
56
- const handleDeleteCard = useCallback(
57
- (card: Card) => {
58
- if (canDeleteCards) {
59
- openModal('deleteCardModal', {
60
- cardId: card.id.toString(),
61
- cardLastDigits: (card.number ?? '').replaceAll(/\D/g, '').slice(-4),
62
- onSuccess: () => {
63
- if (card.id === selectedCardId && cards.length > 1) {
64
- const remainingCards = cards.filter((c) => c.id !== card.id);
65
- if (remainingCards.length > 0) {
66
- onCardSelect(remainingCards[0].id.toString());
67
- }
68
- }
69
- },
70
- });
71
- }
72
- },
73
- [canDeleteCards, openModal, selectedCardId, cards, onCardSelect]
74
- );
75
-
76
62
  const handleSelectCard = useCallback(
77
63
  (cardId: string) => {
78
64
  onCardSelect(cardId);
79
65
  setIsExpanded(false);
80
66
  },
81
- [onCardSelect]
67
+ [onCardSelect],
68
+ );
69
+
70
+ const handleAfterDelete = useCallback(
71
+ (card: Card) => {
72
+ if (card.id.toString() === selectedCardId.toString()) {
73
+ const remaining = cards.filter((c) => c.id !== card.id);
74
+ if (remaining.length > 0) {
75
+ onCardSelect(remaining[0].id.toString());
76
+ }
77
+ }
78
+ },
79
+ [cards, selectedCardId, onCardSelect],
82
80
  );
83
81
 
84
82
  if (!hasCards) {
@@ -123,7 +121,9 @@ export function PaymentInfoCard({
123
121
  <>
124
122
  <span className="paragraph-small-medium text-zinc-500 w-[100px]">Pagamento</span>
125
123
  <div className="flex flex-col gap-0.5 flex-1 min-w-0">
126
- <span className="paragraph-small-semibold text-zinc-950 truncate">{selectedCard?.name}</span>
124
+ <span className="paragraph-small-semibold text-zinc-950 truncate">
125
+ {selectedCard?.name}
126
+ </span>
127
127
  <span className="paragraph-small-medium text-zinc-950">
128
128
  •••• {selectedCard?.number?.replaceAll(/\D/g, '')?.slice(-4)}
129
129
  </span>
@@ -156,9 +156,9 @@ export function PaymentInfoCard({
156
156
  card={card}
157
157
  isSelected={card.id.toString() === selectedCardId.toString()}
158
158
  onSelect={() => handleSelectCard(card.id.toString())}
159
- showMenu
160
- canDelete={canDeleteCards}
161
- onDelete={() => handleDeleteCard(card)}
159
+ hideDelete={hideDelete || onlyOneCard}
160
+ hideSetDefault={hideSetDefault}
161
+ onAfterDelete={handleAfterDelete}
162
162
  />
163
163
  ))}
164
164
  {shouldShowManageButton && onManageCards && (
package/src/index.ts CHANGED
@@ -73,11 +73,15 @@ export { usePlanById } from "./modules/plans/hooks/use-plan-by-id.hook";
73
73
  export { useHasPlanAddon } from "./modules/plans/hooks/use-has-plan-addon.hook";
74
74
  export { useCalculateSubscription } from "./modules/subscriptions/hooks/calculate-subscription.hook";
75
75
  export { useUpdateSubscriptionPlan } from "./modules/subscriptions/hooks/update-subscription-plan.hook";
76
+ export { useUpdateSubscriptionPayment } from "./modules/subscriptions/hooks/update-subscription-payment.hook";
76
77
  export { useCards, CARDS_QUERY_KEY } from "./modules/cards/hooks/cards.hook";
77
78
  export { useCardById } from "./modules/cards/hooks/card-by-id.hook";
78
79
  export { useCreateCard } from "./modules/cards/hooks/create-card.hook";
79
80
  export { useSetDefaultCard } from "./modules/cards/hooks/set-default-card.hook";
80
81
  export { useDeleteCard } from "./modules/cards/hooks/delete-card.hook";
82
+ export { useDeleteConfirmation } from "./modules/cards/hooks/delete-confirmation.hook";
83
+ export type { DeleteConfirmationFormData } from "./modules/cards/hooks/delete-confirmation.hook";
84
+ export { useHandleDeleteCard } from "./modules/cards/hooks/handle-delete-card.hook";
81
85
  export { useIaCredits } from "./modules/ia-credits/hooks/ia-credits.hook";
82
86
  export type {
83
87
  IaCreditsSummary,
@@ -150,6 +154,8 @@ export { usePaidPlanRequiredModal } from "./store/usePaidPlanRequiredModal";
150
154
  export { default as BuyCreditsModal } from "./components/modals/BuyCreditsModal";
151
155
  export { default as PaidPlanRequiredModal } from "./components/modals/PaidPlanRequiredModal";
152
156
  export { default as AddCardModal } from "./components/modals/cards/AddCardModal";
157
+ export { DeleteCardModal } from "./components/modals/cards/DeleteCardModal";
158
+ export { CannotDeleteCardModal } from "./components/modals/cards/CannotDeleteCardModal";
153
159
  export {
154
160
  CardFormFields,
155
161
  cardFormSchema,
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { useCallback } from 'react';
4
+ import { useForm } from 'react-hook-form';
5
+ import { zodResolver } from '@hookform/resolvers/zod';
6
+ import z from 'zod';
7
+
8
+ type UseDeleteConfirmationProps = {
9
+ confirmWord?: string;
10
+ };
11
+
12
+ function createDeleteConfirmationSchema(confirmWord: string) {
13
+ return z.object({
14
+ confirmText: z.string().refine((val) => val === confirmWord, {
15
+ message: `Digite ${confirmWord} para confirmar`,
16
+ }),
17
+ });
18
+ }
19
+
20
+ export type DeleteConfirmationFormData = z.infer<
21
+ ReturnType<typeof createDeleteConfirmationSchema>
22
+ >;
23
+
24
+ export function useDeleteConfirmation({
25
+ confirmWord = 'EXCLUIR',
26
+ }: UseDeleteConfirmationProps = {}) {
27
+ const schema = createDeleteConfirmationSchema(confirmWord);
28
+
29
+ const form = useForm<DeleteConfirmationFormData>({
30
+ resolver: zodResolver(schema),
31
+ defaultValues: { confirmText: '' },
32
+ });
33
+
34
+ const confirmText = form.watch('confirmText');
35
+ const isValidConfirmation = confirmText === confirmWord;
36
+
37
+ const resetConfirmation = useCallback(() => {
38
+ form.reset({ confirmText: '' });
39
+ }, [form]);
40
+
41
+ return { form, confirmText, isValidConfirmation, resetConfirmation };
42
+ }
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { useCallback } from 'react';
4
+ import { useModalManager } from '../../../store/useModalManager';
5
+ import { useActiveSubscription } from '../../subscriptions/hooks/find-active-subscription.hook';
6
+ import type { Card } from '../types';
7
+
8
+ type UseHandleDeleteCardOptions = {
9
+ onAfterDelete?: (card: Card) => void;
10
+ };
11
+
12
+ export function useHandleDeleteCard({
13
+ onAfterDelete,
14
+ }: UseHandleDeleteCardOptions = {}) {
15
+ const { openModal } = useModalManager();
16
+ const { data: subscriptionData } = useActiveSubscription();
17
+ const activeSubscription = subscriptionData?.data?.[0] ?? null;
18
+
19
+ return useCallback(
20
+ (card: Card) => {
21
+ const isCardLinkedToSubscription =
22
+ !!activeSubscription &&
23
+ !!activeSubscription.date_cancellation &&
24
+ (!activeSubscription.date_due ||
25
+ activeSubscription.date_due < new Date()) &&
26
+ card.id.toString() === activeSubscription.id_card?.toString();
27
+
28
+ if (isCardLinkedToSubscription) {
29
+ openModal('cannotDeleteCardModal', {
30
+ cardId: card.id.toString(),
31
+ });
32
+ return;
33
+ }
34
+
35
+ openModal('deleteCardModal', {
36
+ cardId: card.id.toString(),
37
+ ...(onAfterDelete && { onSuccess: () => onAfterDelete(card) }),
38
+ });
39
+ },
40
+ [activeSubscription, openModal, onAfterDelete],
41
+ );
42
+ }
@@ -0,0 +1,13 @@
1
+ "use server";
2
+
3
+ import { safeMutationAction } from "../../../utils/safeMutationAction";
4
+ import { subscriptionsService } from "../services/subscriptions.service";
5
+
6
+ export async function updateSubscriptionPaymentAction(
7
+ subscriptionId: number | string,
8
+ data: { id_card: string; payment_method: number },
9
+ ) {
10
+ return safeMutationAction(async () => {
11
+ return subscriptionsService.updateSubscriptionPayment(subscriptionId, 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 { updateSubscriptionPaymentAction } from '../actions/update-subscription-payment.action';
6
+ import { SUBSCRIPTIONS_QUERY_KEY } from '../constants/query-keys.constants';
7
+ import { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';
8
+
9
+ export function useUpdateSubscriptionPayment() {
10
+ const queryClient = useQueryClient();
11
+ const subscriptionsKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY]);
12
+
13
+ return useMutation({
14
+ mutationFn: ({
15
+ subscriptionId,
16
+ data,
17
+ }: {
18
+ subscriptionId: number | string;
19
+ data: { id_card: string; payment_method: number };
20
+ }) => withAction(updateSubscriptionPaymentAction)(subscriptionId, data),
21
+ onSuccess: () => {
22
+ queryClient.invalidateQueries({ queryKey: subscriptionsKey });
23
+ },
24
+ });
25
+ }
@@ -104,6 +104,29 @@ class SubscriptionsService {
104
104
 
105
105
  return { success: true };
106
106
  }
107
+
108
+ async updateSubscriptionPayment(
109
+ subscriptionId: number | string,
110
+ data: { id_card: string; payment_method: number },
111
+ ): Promise<SuccessResult<void>> {
112
+ await assertManagementPermission("manage_subscriptions");
113
+ const { id_account } = await getUserContext();
114
+
115
+ const response = await api.apps.put<ApiActionResult<void>>(
116
+ `/accounts/${id_account}/subscriptions/${subscriptionId}/action/payment`,
117
+ data,
118
+ );
119
+
120
+ if (response.status === 0) {
121
+ throw new ApiError(
122
+ response.message || "Erro ao atualizar método de pagamento",
123
+ "UPDATE_SUBSCRIPTION_PAYMENT_FAILED",
124
+ 400,
125
+ );
126
+ }
127
+
128
+ return { success: true };
129
+ }
107
130
  }
108
131
 
109
132
  export const subscriptionsService = new SubscriptionsService();
@@ -36,7 +36,7 @@ export const SubscriptionSchema = z.object({
36
36
  periodicity: z.coerce.number().nullable(),
37
37
  value: z.number().nullable(),
38
38
  id_product: z.number(),
39
- id_plan: z.number(),
39
+ id_plan: z.number().nullable(),
40
40
  plan_name: z.string().nullable().optional(),
41
41
  id_gateway: z.union([z.number(), z.string()]).nullable(),
42
42
  gateway_account: z.string().nullable(),
package/src/server.ts CHANGED
@@ -27,6 +27,7 @@ export { findPlanByIdAction } from './modules/plans/actions/find-plan-by-id.acti
27
27
  export { listPlansAction } from './modules/plans/actions/list-plans.action';
28
28
  export { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';
29
29
  export { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';
30
+ export { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';
30
31
  export { listCardsAction } from './modules/cards/actions/list-cards.action';
31
32
  export { createCardAction } from './modules/cards/actions/create-card.action';
32
33
  export { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';