continue: made a payments method list
This commit is contained in:
@@ -5,7 +5,6 @@ import { getSessionData } from '@/app/actions/session';
|
|||||||
|
|
||||||
export async function getPaymentsMethods() {
|
export async function getPaymentsMethods() {
|
||||||
const token = await getSessionData('token');
|
const token = await getSessionData('token');
|
||||||
console.log('getPaymentsMethods');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/api/payments/methods`, {
|
const response = await fetch(`${API_BASE_URL}/api/payments/methods`, {
|
||||||
@@ -19,7 +18,7 @@ export async function getPaymentsMethods() {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const parsed = await response.json();
|
const parsed = await response.json();
|
||||||
console.log(parsed);
|
|
||||||
return parsed;
|
return parsed;
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
@@ -28,3 +27,33 @@ export async function getPaymentsMethods() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deletePaymentsMethods(cardId: string): Promise<boolean | undefined> {
|
||||||
|
if (!cardId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const token = await getSessionData('token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/payments/methods/${cardId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
if (parsed.message === 'Payment method deleted') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getPaymentsMethods } from '@/app/actions/paymentAction';
|
import { getPaymentsMethods } from '@/app/actions/paymentAction';
|
||||||
|
|
||||||
/* export interface PaymentsMethods {
|
export interface PaymentsMethods {
|
||||||
total_violations: number,
|
cardType: string,
|
||||||
new_violations: number,
|
expiryMonth: string,
|
||||||
in_progress_violations: number,
|
expiryYear: string,
|
||||||
resolved_violations: number
|
lastFour: string,
|
||||||
} */
|
paymentMethodId: string
|
||||||
|
}
|
||||||
|
|
||||||
export const usePaymentsMethods = () => {
|
export const usePaymentsMethods = () => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -14,7 +15,7 @@ export const usePaymentsMethods = () => {
|
|||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
return getPaymentsMethods()
|
return getPaymentsMethods()
|
||||||
},
|
},
|
||||||
select: (data: any | null) => {
|
select: (data: PaymentsMethods[] | null) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,10 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
@@ -190,6 +194,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-warning {
|
||||||
|
background: v.$color-warning;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: v.$color-warning-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
|
|||||||
@@ -5250,8 +5250,128 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.payment-user-cards {
|
.payment-user-cards {
|
||||||
.card-wrapper {
|
h3 {
|
||||||
|
color: #1e293b;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-wrapper {
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1.5px dashed #d1d5db;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: 14px;
|
||||||
|
|
||||||
|
&-info {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
color: v.$text-s;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-type {
|
||||||
|
font-size: 10px;
|
||||||
|
color: v.$text-s;
|
||||||
|
margin-left: 5px;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
/* top: 3px; */
|
||||||
|
|
||||||
|
@media (max-width: 490px) {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-expired {
|
||||||
|
font-size: 10px;
|
||||||
|
color: v.$text-s;
|
||||||
|
margin-left: 5px;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 3px;
|
||||||
|
|
||||||
|
@media (max-width: 490px) {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-confirm-window {
|
||||||
|
h4 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-confirm-action {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
max-width: 140px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e2e4e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: v.$white;
|
||||||
|
padding: 5px 10px;
|
||||||
|
|
||||||
|
@media (max-width: 490px) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-empty {
|
||||||
|
.empty-card-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #64748b;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-card-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9ca3af;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-notification {
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #9ca3af;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,8 @@ $p-color-disabled: #6365f18e;
|
|||||||
$s-color-disabled: #8a5cf663;
|
$s-color-disabled: #8a5cf663;
|
||||||
$border-color-1: #e2e8f0;
|
$border-color-1: #e2e8f0;
|
||||||
$border-color-1-hover: #b1b7be;
|
$border-color-1-hover: #b1b7be;
|
||||||
|
$color-warning: #fab005;
|
||||||
|
$color-warning-hover: #fa9805;
|
||||||
|
|
||||||
$color-image: #f08c00;
|
$color-image: #f08c00;
|
||||||
$color-video: #2f9e44;
|
$color-video: #2f9e44;
|
||||||
|
|||||||
@@ -2,36 +2,197 @@
|
|||||||
|
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
import BankCardInput from '@/app/ui/inputs/bank-card-inpit';
|
import BankCardInput from '@/app/ui/inputs/bank-card-inpit';
|
||||||
import { usePaymentsMethods } from '@/app/hooks/react-query/usePaymentsMethods';
|
import { usePaymentsMethods, PaymentsMethods } from '@/app/hooks/react-query/usePaymentsMethods';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { deletePaymentsMethods } from '@/app/actions/paymentAction';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
import ModalWindow from '@/app/components/ModalWindow';
|
||||||
|
|
||||||
export default function PaymentUserCards() {
|
export default function PaymentUserCards() {
|
||||||
const t = useTranslations('Global');
|
const t = useTranslations('Global');
|
||||||
const { data: paymentsMethods } = usePaymentsMethods();
|
const { data: paymentsMethods } = usePaymentsMethods();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const [openWindow, setOpenWindow] = useState<boolean>(false);
|
||||||
|
const [selectedCard, setSelectedCard] = useState<PaymentsMethods | null>(null);
|
||||||
|
const [modalLoading, setModalLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
function openConfirmWindowHandler(item: PaymentsMethods) {
|
||||||
console.log(paymentsMethods);
|
setOpenWindow(true);
|
||||||
}, [paymentsMethods])
|
setSelectedCard(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeCardHandler(id: string) {
|
||||||
|
setModalLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await deletePaymentsMethods(id);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['paymentsMethods'] });
|
||||||
|
toast.success('bank-card-has-been-successfully-deleted');
|
||||||
|
} else {
|
||||||
|
throw 'error'
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.warning(t('failed-to-delete-bank-card'))
|
||||||
|
} finally {
|
||||||
|
setModalLoading(false);
|
||||||
|
setOpenWindow(false);
|
||||||
|
setSelectedCard(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfirmModalWindow = () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="card-item-confirm-window"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
{t('delete-a-bank-card')}?
|
||||||
|
</h4>
|
||||||
|
<div
|
||||||
|
className="card-item-confirm-action"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedCard(null);
|
||||||
|
setOpenWindow(false);
|
||||||
|
}}
|
||||||
|
disabled={modalLoading}
|
||||||
|
>
|
||||||
|
{t('no')}
|
||||||
|
{modalLoading && (
|
||||||
|
<div
|
||||||
|
className="loading-animation"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="global-spinner"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() => {
|
||||||
|
if (selectedCard?.paymentMethodId) {
|
||||||
|
removeCardHandler(selectedCard?.paymentMethodId)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={modalLoading}
|
||||||
|
>
|
||||||
|
{t('yes')}
|
||||||
|
{modalLoading && (
|
||||||
|
<div
|
||||||
|
className="loading-animation"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="global-spinner"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="block-wrapper payment-user-cards"
|
className="block-wrapper payment-user-cards"
|
||||||
>
|
>
|
||||||
<div
|
<ModalWindow state={openWindow} callBack={setOpenWindow}>
|
||||||
className="card-wrapper"
|
<ConfirmModalWindow />
|
||||||
>
|
</ModalWindow>
|
||||||
<BankCardInput
|
<h3>
|
||||||
bankCardState={'1111 1111 1111 1111'}
|
{t('linked-bank-cards')}
|
||||||
validateHandler={() => { }}
|
</h3>
|
||||||
disabled={true}
|
|
||||||
isValid={undefined}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
<div
|
||||||
className="btn btn-primary"
|
className="cards-wrapper"
|
||||||
>
|
>
|
||||||
{t('remove')}
|
{paymentsMethods?.length !== 0 ? (
|
||||||
</button>
|
paymentsMethods?.map((item: PaymentsMethods) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="card-item"
|
||||||
|
key={item?.paymentMethodId}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="card-item-info"
|
||||||
|
>
|
||||||
|
<BankCardInput
|
||||||
|
bankCardState={item?.lastFour}
|
||||||
|
validateHandler={() => { }}
|
||||||
|
disabled={true}
|
||||||
|
isValid={undefined}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span
|
||||||
|
className="card-item-type"
|
||||||
|
>
|
||||||
|
{item?.cardType}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* <span
|
||||||
|
className="card-item-expired"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="ml-1"
|
||||||
|
>
|
||||||
|
expired at:
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{item?.expiryMonth}/
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{item?.expiryYear}
|
||||||
|
</span>
|
||||||
|
</span> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn btn-warning"
|
||||||
|
onClick={() => {
|
||||||
|
openConfirmWindowHandler(item)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('remove')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="payment-user-cards-empty"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p
|
||||||
|
className="empty-card-title"
|
||||||
|
>
|
||||||
|
{t('the-card-is-not-linked')}
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
className="empty-card-text"
|
||||||
|
>
|
||||||
|
{t('the-card-is-linked-automatically-the-next-time-you-pay-for-a-subscription')}
|
||||||
|
<br />
|
||||||
|
{t('after-this-the-subscription-will-renew-automatically')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="payment-user-cards-notification"
|
||||||
|
>
|
||||||
|
{t('when-you-pay-for-a-subscription-your-card-is-saved-for-automatic-renewal')}
|
||||||
|
{t('the-debit-occurs-1-day-before-the-end-of-the-subscription')}
|
||||||
|
{t('you-can-unlink-your-card-and-cancel-auto-renewal-at-any-time')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -390,7 +390,17 @@
|
|||||||
"select-all": "Select all",
|
"select-all": "Select all",
|
||||||
"text-area-error-fill-complaint-text": "Fill out the complaint text",
|
"text-area-error-fill-complaint-text": "Fill out the complaint text",
|
||||||
"to-view-a-file-match-click-on-the-card-from-the-list": "To view a file match, click on the card from the list",
|
"to-view-a-file-match-click-on-the-card-from-the-list": "To view a file match, click on the card from the list",
|
||||||
"insufficient-funds-to-receive-payment": "Insufficient funds to receive payment"
|
"insufficient-funds-to-receive-payment": "Insufficient funds to receive payment",
|
||||||
|
"failed-to-delete-bank-card": "Failed to delete bank card",
|
||||||
|
"bank-card-has-been-successfully-deleted": "The bank card has been successfully deleted",
|
||||||
|
"linked-bank-cards": "Linked bank cards",
|
||||||
|
"the-card-is-not-linked": "The card is not linked",
|
||||||
|
"the-card-is-linked-automatically-the-next-time-you-pay-for-a-subscription": "The card is linked automatically the next time you pay for a subscription.",
|
||||||
|
"after-this-the-subscription-will-renew-automatically": "After this, the subscription will renew automatically.",
|
||||||
|
"when-you-pay-for-a-subscription-your-card-is-saved-for-automatic-renewal": "When you pay for a subscription, your card is saved for automatic renewal.",
|
||||||
|
"the-debit-occurs-1-day-before-the-end-of-the-subscription": "The debit occurs 1 day before the end of the subscription.",
|
||||||
|
"you-can-unlink-your-card-and-cancel-auto-renewal-at-any-time": "You can unlink your card and cancel auto-renewal at any time.",
|
||||||
|
"delete-a-bank-card": "Delete a bank card"
|
||||||
},
|
},
|
||||||
"Login-register-form": {
|
"Login-register-form": {
|
||||||
"and": "and",
|
"and": "and",
|
||||||
|
|||||||
@@ -390,7 +390,17 @@
|
|||||||
"select-all": "Выбрать все",
|
"select-all": "Выбрать все",
|
||||||
"text-area-error-fill-complaint-text": "Заполните текст жалобы",
|
"text-area-error-fill-complaint-text": "Заполните текст жалобы",
|
||||||
"to-view-a-file-match-click-on-the-card-from-the-list": "Для просмотра совпадения по файлу нажмите на карточку из списка",
|
"to-view-a-file-match-click-on-the-card-from-the-list": "Для просмотра совпадения по файлу нажмите на карточку из списка",
|
||||||
"insufficient-funds-to-receive-payment": "Недостаточно средств для получения выплаты"
|
"insufficient-funds-to-receive-payment": "Недостаточно средств для получения выплаты",
|
||||||
|
"failed-to-delete-bank-card": "Не удалось удалить банковскую карту",
|
||||||
|
"bank-card-has-been-successfully-deleted": "Банковская карта успешно удалена",
|
||||||
|
"linked-bank-cards": "Привязанные банковские карты",
|
||||||
|
"the-card-is-not-linked": "Карта не привязана",
|
||||||
|
"the-card-is-linked-automatically-the-next-time-you-pay-for-a-subscription": "Карта привязывается автоматически при следующей оплате подписки.",
|
||||||
|
"after-this-the-subscription-will-renew-automatically": "После этого подписка будет продлеваться автоматически.",
|
||||||
|
"when-you-pay-for-a-subscription-your-card-is-saved-for-automatic-renewal": "При оплате подписки ваша карта сохраняется для автоматического продления.",
|
||||||
|
"the-debit-occurs-1-day-before-the-end-of-the-subscription": "Списание происходит за 1 день до окончания подписки.",
|
||||||
|
"you-can-unlink-your-card-and-cancel-auto-renewal-at-any-time": "Вы можете отвязать карту и отменить автопродление в любой момент.",
|
||||||
|
"delete-a-bank-card": "Удалить банковскую карту"
|
||||||
},
|
},
|
||||||
"Login-register-form": {
|
"Login-register-form": {
|
||||||
"and": "и",
|
"and": "и",
|
||||||
|
|||||||
Reference in New Issue
Block a user