diff --git a/src/components/UI/components/TabPanel.js b/src/components/UI/components/TabPanel.js
new file mode 100644
index 0000000..90490fd
--- /dev/null
+++ b/src/components/UI/components/TabPanel.js
@@ -0,0 +1,19 @@
+import { Box } from '@mui/material';
+
+const TabPanel = (props) => {
+ const { children, value, index, ...other } = props;
+
+ return (
+
+ {value === index && {children}}
+
+ );
+}
+
+export default TabPanel;
\ No newline at end of file
diff --git a/src/pages/SendTokens/SendTokens.js b/src/pages/SendTokens/SendTokens.js
index 41febfc..eda96e9 100644
--- a/src/pages/SendTokens/SendTokens.js
+++ b/src/pages/SendTokens/SendTokens.js
@@ -1,5 +1,5 @@
-import React, { useContext, useState } from 'react';
-import { Paper } from '@mui/material';
+import { useContext, useState } from 'react';
+import { Paper, Tab, Tabs } from '@mui/material';
import {
ContentContainer,
LoaderContainer,
@@ -10,23 +10,36 @@ import SendTokensForm from './SendTokensForm/SendTokensForm';
import Message, {
MessageType,
} from '../../components/UI/components/Message/Message';
-import apiClient from '../../utils/apiClient';
import { Loader } from '../../components/UI/components/Loader/Loader';
import TokenInfoBlock from './TokenInfoBlock/TokenInfoBlock';
-import { formatWithCommas } from '../../utils/formatting';
+import SendToUntrustedWalletsForm from './SendTokensForm/SendToUntrustedWallets';
import AuthContext from '../../store/auth-context';
+import TabPanel from '../../components/UI/components/TabPanel'
+import { handleCreateWallet } from './helpers/walletHandlers';
+import { formatWithCommas } from '../../utils/formatting';
+import { handleSendToUntrustedWallets } from './helpers/sendTokenHandlers';
+import apiClient from '../../utils/apiClient';
+
+
const SendTokens = () => {
const [createdWalletName, setCreatedWalletName] = useState();
const [errorMessage, setErrorMessage] = useState();
const [successMessage, setSuccessMessage] = useState();
const [isLoading, setIsLoading] = useState(false);
+ const [tabValue, setTabValue] = useState(0);
const [senderWalletName, setSenderWalletName] = useState();
const [senderWalletTokens, setSenderWalletTokens] = useState(0);
const authContext = useContext(AuthContext);
+ const handleTabChange = (event, newValue) => {
+ setTabValue(newValue);
+ };
+
+
+
// TODO: uncomment when API is ready: is should have a totalTokens value
// const [totalTokensAmount, setTotalTokensAmount] = useState();
@@ -101,34 +114,14 @@ const SendTokens = () => {
});
};
- const handleCreateWalled = (name) => {
- if (!name) return;
-
- setIsLoading(true);
+
- apiClient
- .setAuthHeader(authContext.token)
- .post('/wallets', {
- wallet: name,
- })
- .then(() => {
- setErrorMessage('');
- setSuccessMessage(`Wallet ${name} created successfully!`);
- setCreatedWalletName(name);
- })
- .catch((error) => {
- console.error(error);
- setSuccessMessage('');
- const errorMessage =
- error.response.status === 403 &&
- error.response.data.message.includes('already exists')
- ? 'Wallet with this name already exists.'
- : 'An error occurred while creating a wallet.';
- setErrorMessage(errorMessage);
- })
- .finally(() => {
- setIsLoading(false);
- });
+ const callbacks = {
+ setIsLoading,
+ setErrorMessage,
+ setSuccessMessage,
+ setSenderWalletTokens,
+ setCreatedWalletName
};
return (
@@ -158,38 +151,75 @@ const SendTokens = () => {
width: '100%',
height: '60vh',
display: 'flex',
- justifyContent: 'space-between',
+ flexDirection: 'column',
}}
>
+
+
+
+
+
+
{isLoading && (
)}
- {
- if (!wallet) {
- setSenderWalletName(null);
- setSenderWalletTokens(null);
- return;
- }
-
- setSenderWalletName(wallet.name);
- setSenderWalletTokens(wallet.tokensInWallet);
- }}
- />
-
+
+
+
+ handleSendTokenForm(data, authContext, callbacks)}
+ createdWalletName={createdWalletName}
+ onCreateWallet={(name) => handleCreateWallet(name, authContext, callbacks)}
+ onSenderWalletSelected={(wallet) => {
+ if (!wallet) {
+ setSenderWalletName(null);
+ setSenderWalletTokens(null);
+ return;
+ }
+ setSenderWalletName(wallet.name);
+ setSenderWalletTokens(wallet.tokensInWallet);
+ }}
+ />
+
+
+
+
+
+
+
Under development
+ {/* To be implemented */}
+
+
+
+
+
+ handleSendToUntrustedWallets(data, authContext, callbacks)}
+ onSenderWalletSelected={(wallet) => {
+ if (!wallet) {
+ setSenderWalletName(null);
+ setSenderWalletTokens(null);
+ return;
+ }
+ setSenderWalletName(wallet?.name);
+ setSenderWalletTokens(wallet?.tokensInWallet);
+ }}
+ />
+
+
+
);
};
-export default SendTokens;
+export default SendTokens;
\ No newline at end of file
diff --git a/src/pages/SendTokens/SendTokensForm/SendToUntrustedWallets.js b/src/pages/SendTokens/SendTokensForm/SendToUntrustedWallets.js
new file mode 100644
index 0000000..75fa171
--- /dev/null
+++ b/src/pages/SendTokens/SendTokensForm/SendToUntrustedWallets.js
@@ -0,0 +1,137 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import { Grid, TextField } from '@mui/material';
+import SelectWallet from './SelectWallet';
+import { StyledBox, StyledButton } from './SendTokensFormStyled';
+import ConfirmDialog from './confirmDialog/ConfirmDialog';
+
+const SendToUntrustedWalletsForm = (props) => {
+ const {
+ onSubmit,
+ onSenderWalletSelected,
+ } = props;
+
+ const [senderWallet, setSenderWallet] = useState(null);
+ const [receiverWallet, setReceiverWallet] = useState('');
+ const tokensAmountRef = useRef(0);
+ const [isSubmitBtnDisabled, setIsSubmitButtonDisabled] = useState(true);
+ const [showConfirmationDialog, setShowConfirmationDialog] = useState(false);
+
+ useEffect(() => {
+ isSubmitButtonDisabled();
+ }, [receiverWallet, senderWallet]);
+
+ const handleConfirmationDialogOpen = e => {
+ e.preventDefault();
+ setShowConfirmationDialog(true);
+ };
+
+ const handleConfirmationDialogClose = () => {
+ setShowConfirmationDialog(false);
+ };
+
+ const handleConfirmSubmit = () => {
+ handleSubmit();
+ handleConfirmationDialogClose();
+ };
+
+ const handleChangeSenderWallet = useCallback((wallet) => {
+ if (!wallet) {
+ setSenderWallet(null);
+ onSenderWalletSelected(null);
+ return;
+ }
+
+ setSenderWallet(wallet.name);
+ onSenderWalletSelected(wallet);
+
+ if (tokensAmountRef.current.value > wallet.tokensInWallet) {
+ tokensAmountRef.current.value = wallet.tokensInWallet;
+ }
+ tokensAmountRef.current.setAttribute('max', wallet.tokensInWallet);
+ }, []);
+
+ const handleReceiverWalletChange = (e) => {
+ setReceiverWallet(e.target.value);
+ };
+
+ const handleSubmit = () => {
+ const tokensAmount = tokensAmountRef.current.value;
+
+ onSubmit({
+ senderWallet,
+ receiverWallet,
+ tokensAmount,
+ });
+
+ // reset form
+ tokensAmountRef.current.value = 1;
+ };
+
+ const isSubmitButtonDisabled = () => {
+ const isDisabled =
+ !senderWallet ||
+ !receiverWallet ||
+ tokensAmountRef.current.value <= 0;
+ setIsSubmitButtonDisabled(isDisabled);
+ };
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default React.memo(SendToUntrustedWalletsForm);
\ No newline at end of file
diff --git a/src/pages/SendTokens/helpers/sendTokenHandlers.js b/src/pages/SendTokens/helpers/sendTokenHandlers.js
new file mode 100644
index 0000000..e5eb178
--- /dev/null
+++ b/src/pages/SendTokens/helpers/sendTokenHandlers.js
@@ -0,0 +1,89 @@
+import apiClient from "../../../utils/apiClient";
+
+
+
+export const handleSendTokenForm = async (data, authContext, callbacks) => {
+ const { setIsLoading, setErrorMessage, setSuccessMessage, setSenderWalletTokens } = callbacks;
+
+ setIsLoading(true);
+
+ try {
+ const response = await apiClient
+ .setAuthHeader(authContext.token)
+ .post('/transfers', {
+ bundle: { bundle_size: data.tokensAmount },
+ sender_wallet: data.senderWallet,
+ receiver_wallet: data.receiverWallet,
+ claim: false,
+ });
+
+ console.log(
+ 'Tokens transfer completed. Response: ' + JSON.stringify(response)
+ );
+
+ setSenderWalletTokens((prev) => prev - data.tokensAmount);
+ setErrorMessage('');
+ setSuccessMessage(
+ `${data.tokensAmount} tokens were successfully sent from '${data.senderWallet}' to '${data.receiverWallet}' wallet. Status of the transfer: '${response.data.state}'`
+ );
+ } catch (error) {
+ console.error(error);
+ setSuccessMessage('');
+ const errorMessage =
+ error.response?.data?.message ===
+ 'Cannot transfer to the same wallet as the originating one!'
+ ? error.response.data.message
+ : 'An error occurred while sending tokens.';
+ setErrorMessage(errorMessage);
+ } finally {
+ setIsLoading(false);
+ }
+};
+
+export const handleSendToUntrustedWallets = async (data, authContext, callbacks) => {
+ const { setIsLoading, setErrorMessage, setSuccessMessage, setSenderWalletTokens } = callbacks;
+
+ setIsLoading(true);
+
+ try {
+ const response = await apiClient
+ .setAuthHeader(authContext.token)
+ .post('/transfers', {
+ bundle: { bundle_size: data.tokensAmount },
+ sender_wallet: data.senderWallet,
+ receiver_wallet: data.receiverWallet,
+ claim: false,
+ });
+
+ console.log(
+ 'Tokens transfer to untrusted wallet completed. Response: ' + JSON.stringify(response)
+ );
+
+ setSenderWalletTokens((prev) => prev - data.tokensAmount);
+ setErrorMessage('');
+ setSuccessMessage(
+ `${data.tokensAmount} tokens were successfully sent to untrusted wallet '${data.receiverWallet}'. ` +
+ `Status: '${response.data.state}'. ` +
+ `Note: This wallet is not trusted - please verify with the recipient.`
+ );
+ } catch (error) {
+ console.error('Error sending to untrusted wallet:', error);
+ setSuccessMessage('');
+
+ let errorMessage = 'An error occurred while sending tokens to untrusted wallet.';
+
+ if (error.response) {
+ if (error.response.data.message === 'Cannot transfer to the same wallet as the originating one!') {
+ errorMessage = error.response.data.message;
+ } else if (error.response.data.message.includes('invalid wallet address')) {
+ errorMessage = 'Invalid wallet address format. Please check the receiver wallet address.';
+ } else if (error.response.status === 403) {
+ errorMessage = 'You are not authorized to send to this wallet.';
+ }
+ }
+
+ setErrorMessage(errorMessage);
+ } finally {
+ setIsLoading(false);
+ }
+};
\ No newline at end of file
diff --git a/src/pages/SendTokens/helpers/walletHandlers.js b/src/pages/SendTokens/helpers/walletHandlers.js
new file mode 100644
index 0000000..a81cb22
--- /dev/null
+++ b/src/pages/SendTokens/helpers/walletHandlers.js
@@ -0,0 +1,33 @@
+import apiClient from "../../../utils/apiClient";
+
+
+export const handleCreateWallet = async (name, authContext, callbacks) => {
+ const { setIsLoading, setErrorMessage, setSuccessMessage, setCreatedWalletName } = callbacks;
+
+ if (!name) return;
+
+ setIsLoading(true);
+
+ try {
+ await apiClient
+ .setAuthHeader(authContext.token)
+ .post('/wallets', {
+ wallet: name,
+ });
+
+ setErrorMessage('');
+ setSuccessMessage(`Wallet ${name} created successfully!`);
+ setCreatedWalletName(name);
+ } catch (error) {
+ console.error(error);
+ setSuccessMessage('');
+ const errorMessage =
+ error.response?.status === 403 &&
+ error.response?.data?.message?.includes('already exists')
+ ? 'Wallet with this name already exists.'
+ : 'An error occurred while creating a wallet.';
+ setErrorMessage(errorMessage);
+ } finally {
+ setIsLoading(false);
+ }
+};
\ No newline at end of file