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() {
|
||||
const token = await getSessionData('token');
|
||||
console.log('getPaymentsMethods');
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/payments/methods`, {
|
||||
@@ -19,7 +18,7 @@ export async function getPaymentsMethods() {
|
||||
|
||||
if (response.ok) {
|
||||
const parsed = await response.json();
|
||||
console.log(parsed);
|
||||
|
||||
return parsed;
|
||||
} else {
|
||||
return null
|
||||
@@ -27,4 +26,34 @@ export async function getPaymentsMethods() {
|
||||
} catch (error) {
|
||||
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 { getPaymentsMethods } from '@/app/actions/paymentAction';
|
||||
|
||||
/* export interface PaymentsMethods {
|
||||
total_violations: number,
|
||||
new_violations: number,
|
||||
in_progress_violations: number,
|
||||
resolved_violations: number
|
||||
} */
|
||||
export interface PaymentsMethods {
|
||||
cardType: string,
|
||||
expiryMonth: string,
|
||||
expiryYear: string,
|
||||
lastFour: string,
|
||||
paymentMethodId: string
|
||||
}
|
||||
|
||||
export const usePaymentsMethods = () => {
|
||||
return useQuery({
|
||||
@@ -14,7 +15,7 @@ export const usePaymentsMethods = () => {
|
||||
queryFn: () => {
|
||||
return getPaymentsMethods()
|
||||
},
|
||||
select: (data: any | null) => {
|
||||
select: (data: PaymentsMethods[] | null) => {
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -151,6 +151,10 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@@ -190,6 +194,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: v.$color-warning;
|
||||
|
||||
&:hover {
|
||||
background: v.$color-warning-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
|
||||
@@ -5250,8 +5250,128 @@
|
||||
}
|
||||
|
||||
.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;
|
||||
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;
|
||||
$border-color-1: #e2e8f0;
|
||||
$border-color-1-hover: #b1b7be;
|
||||
$color-warning: #fab005;
|
||||
$color-warning-hover: #fa9805;
|
||||
|
||||
$color-image: #f08c00;
|
||||
$color-video: #2f9e44;
|
||||
|
||||
@@ -2,36 +2,197 @@
|
||||
|
||||
import { useTranslations } from 'next-intl'
|
||||
import BankCardInput from '@/app/ui/inputs/bank-card-inpit';
|
||||
import { usePaymentsMethods } from '@/app/hooks/react-query/usePaymentsMethods';
|
||||
import { useEffect } from 'react';
|
||||
import { usePaymentsMethods, PaymentsMethods } from '@/app/hooks/react-query/usePaymentsMethods';
|
||||
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() {
|
||||
const t = useTranslations('Global');
|
||||
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(() => {
|
||||
console.log(paymentsMethods);
|
||||
}, [paymentsMethods])
|
||||
function openConfirmWindowHandler(item: PaymentsMethods) {
|
||||
setOpenWindow(true);
|
||||
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 (
|
||||
<div
|
||||
className="block-wrapper payment-user-cards"
|
||||
>
|
||||
<div
|
||||
className="card-wrapper"
|
||||
>
|
||||
<BankCardInput
|
||||
bankCardState={'1111 1111 1111 1111'}
|
||||
validateHandler={() => { }}
|
||||
disabled={true}
|
||||
isValid={undefined}
|
||||
/>
|
||||
<ModalWindow state={openWindow} callBack={setOpenWindow}>
|
||||
<ConfirmModalWindow />
|
||||
</ModalWindow>
|
||||
<h3>
|
||||
{t('linked-bank-cards')}
|
||||
</h3>
|
||||
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
>
|
||||
{t('remove')}
|
||||
</button>
|
||||
<div
|
||||
className="cards-wrapper"
|
||||
>
|
||||
{paymentsMethods?.length !== 0 ? (
|
||||
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>
|
||||
)
|
||||
|
||||
@@ -390,7 +390,17 @@
|
||||
"select-all": "Select all",
|
||||
"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",
|
||||
"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": {
|
||||
"and": "and",
|
||||
|
||||
@@ -390,7 +390,17 @@
|
||||
"select-all": "Выбрать все",
|
||||
"text-area-error-fill-complaint-text": "Заполните текст жалобы",
|
||||
"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": {
|
||||
"and": "и",
|
||||
|
||||
Reference in New Issue
Block a user