Untitled
unknown
plain_text
a year ago
16 kB
15
Indexable
'use client';
import React, { useState, useEffect, useCallback } from 'react';
import {
Button,
Box,
Dialog,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
CircularProgress,
Snackbar,
Chip,
IconButton
} from '@mui/material';
import MainCard from 'ui-component/cards/MainCard';
import AddAccountsForm from './AddAccountsForm';
import axiosInstance from '../../utils/axios';
import MuiAlert from '@mui/material/Alert';
import { useDispatch } from 'react-redux';
import { openSnackbar } from 'store/slices/snackbar';
import { useAccounts } from 'contexts/AccountsContext';
import DeleteIcon from '@mui/icons-material/Delete';
import ConfirmDeleteDialog from '../../components/ConfirmDeleteDialog';
import { useRouter } from 'next/navigation';
import { useSearchParams } from 'next/navigation';
import { FormattedMessage } from 'react-intl';
import Joyride, { Step } from 'react-joyride';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
import { useDashboard } from 'contexts/DashboardContext';
// Subscription
interface SubscriptionAccount {
allowedAccounts: number;
usedAccounts: number;
}
// Data type
export type AccountData = {
id: number;
number: string;
name: string;
account_type: 'Live' | 'Demo';
platform_name: string;
connection: boolean;
status: string;
balance: number | string;
last_sync: string; // Add this line
};
const getStatusColor = (status: string) => {
switch (status) {
case 'Connected':
return 'success';
case 'Disconnected':
return 'error';
case 'Connecting':
return 'default'; // Grey color
default:
return 'default';
}
};
interface AccountsProps {
isNewUser?: boolean;
}
const AccountsTable: React.FC<AccountsProps> = ({ isNewUser }) => {
const [open, setOpen] = useState(false);
const { accountsData, setAccountsData } = useAccounts();
const { setAccountId } = useDashboard();
const searchParams = useSearchParams();
const dispatch = useDispatch();
const router = useRouter(); // Initialize useRouter
const [subscriptionAccount, setSubscriptionAccount] = useState<SubscriptionAccount>({
allowedAccounts: 5,
usedAccounts: 0
}); // ToDo: Set allowed accounts limit based on plan
const [runTour, setRunTour] = useState(false);
const steps: Step[] = [
{
target: 'body',
content: "Welcome to TraderWaves! 🎉 Let's get started by adding your first trading account",
placement: 'center'
},
{
target: '.add-account-button',
content: 'Click here to add an account',
placement: 'bottom'
},
// ToDo: Add the folllowing steps to the tour
{
target: '.name-field',
content: 'Give your account a memorable name',
placement: 'bottom',
disableBeacon: true
},
{
target: '.broker-server-fields',
content: 'We support all MT5 brokers',
placement: 'bottom',
disableBeacon: true
},
{
target: '.login-fields',
content: 'Type your account login details, usually sent to you in an email by your broker',
placement: 'bottom',
disableBeacon: true
}
];
const handleOpenTour = () => {
setRunTour(true);
};
// Add this useEffect to start the tour when isNewUser is true
useEffect(() => {
if (isNewUser) {
setRunTour(true);
}
}, [isNewUser]);
useEffect(() => {
// Check if the query parameter openDialog is true
if (searchParams.get('openDialog') === 'true') {
setOpen(true);
}
}, [searchParams]);
const handleOpen = useCallback(() => {
if (subscriptionAccount.usedAccounts >= subscriptionAccount.allowedAccounts) {
dispatch(
openSnackbar({
open: true,
message: <FormattedMessage id="upgrade-your-plan-add-more" />,
variant: 'alert',
alert: {
color: 'error'
},
close: false
})
);
} else {
setOpen(true);
}
}, [subscriptionAccount, dispatch]);
const handleClose = () => {
setOpen(false);
router.push('/accounts'); // Update URL to remove openAddForm parameter
};
const handleCloseError = () => setError(null);
useEffect(() => {
if (searchParams.get('openAddForm') === 'true') {
handleOpen();
}
}, [searchParams, handleOpen]);
const handleCloseDelete = () => {
setOpenDelete(false);
};
const [openDelete, setOpenDelete] = useState(false);
const [selectedId, setSelectedId] = useState<number | null>(null);
const [loadingDelete, setLoadingDelete] = useState(false);
const handleClickOpen = (id: number) => {
setSelectedId(id);
setOpenDelete(true);
};
const handleDelete = async () => {
if (!selectedId) {
return;
}
try {
setLoadingDelete(true);
// Fetch the signal copy relationships for the account
const response = await axiosInstance.post('/get-slaves-signal-copy');
const data = response.data;
if (data.status === 'success') {
const signalCopies = data.SlavesSignalCopyAccounts.filter((account: any) => account.slave_id === selectedId);
// Delete each signal copy relationship
for (const signalCopy of signalCopies) {
try {
const deleteResponse = await axiosInstance.post('/delete-slave', { id_copy: signalCopy.id });
const deleteData = deleteResponse.data;
if (deleteData.status !== 'success') {
console.error(`Error deleting signal copy with id ${signalCopy.id}:`, deleteData.message);
} else {
console.log(`Signal copy with id ${signalCopy.id} deleted successfully`);
}
} catch (error) {
console.error(`Error deleting signal copy with id ${signalCopy.id}:`, error);
}
}
} else {
console.error('Error fetching signal copy relationships:', data.message);
}
// Proceed to delete the account
const accountDeleteResponse = await axiosInstance.post('/delete-account', { id: selectedId });
const accountDeleteData = accountDeleteResponse.data;
if (accountDeleteData.status !== 'success') {
console.log('Error deleting account:', accountDeleteData.message);
} else {
setAccountsData((prevData) => prevData.filter((account) => account.id !== selectedId));
updateSubscriptionAccount(subscriptionAccount.usedAccounts - 1); // Update the used accounts
console.log('Account deleted successfully');
}
} catch (error) {
setError('Error deleting account');
console.error('Error:', error);
} finally {
setLoadingDelete(false);
handleCloseDelete();
}
};
//For every add and delete account update the combo box in dashboard
useEffect(() => {
if (accountsData.length > 0) {
setAccountId(accountsData[0].id);
}
}, [accountsData, setAccountId]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchSubscriptionData = async () => {
try {
const response = await axiosInstance.post('/get-accounts');
const data = response.data;
if (response.status !== 200 || data.status !== 'success') {
console.error('Error fetching account data:', response.data);
setError('Error fetching account data: ' + data.status);
return;
}
const usedAccounts = data.accounts ? data.accounts.length : 0;
setSubscriptionAccount({
allowedAccounts: 5,
usedAccounts: usedAccounts
});
} catch (error) {
console.error('Error fetching subscription data:', error);
setError('Error fetching subscription data');
}
};
const fetchAccountDetails = async (accountId: number) => {
try {
const response = await axiosInstance.post('/get-account-details', { id: accountId });
const data = response.data;
if (response.status !== 200 || data.status !== 'success') {
console.error('Error fetching account details:', data);
return null;
}
return {
balance: data.balance,
account_type: data.account_type,
connection_status: data.connection_status
};
} catch (error) {
console.error('Error fetching account details:', error);
return null;
}
};
const fetchAccountData = async () => {
if (open) {
return;
}
try {
setLoading(true);
const response = await axiosInstance.post('/get-accounts');
const data = response.data;
if (response.status !== 200 || data.status !== 'success') {
console.error('Error fetching account data:', response.data);
setError('Error fetching account data: ' + data.status);
return;
}
if (data.accounts) {
const accounts = data.accounts;
const accountsData: AccountData[] = accounts.map((account: Partial<AccountData>) => ({
id: account.id!,
number: account.number!,
name: account.name!,
account_type: account.account_type!,
platform_name: account.platform_name!,
connection: false,
status: 'Connecting',
balance: '-',
last_sync: account.last_sync || '' // Add this line
}));
setAccountsData(accountsData);
// Fetch account details asynchronously
accountsData.forEach(async (account) => {
const details = await fetchAccountDetails(account.id);
if (details) {
setAccountsData((prevData) =>
prevData.map((acc) =>
acc.id === account.id
? {
...acc,
balance: details.balance,
account_type: details.account_type,
connection: true,
status: details.connection_status
}
: acc
)
);
}
});
}
} catch (error) {
console.error('Error fetching account data:', error);
setError('Error fetching account data');
} finally {
setLoading(false);
}
};
fetchSubscriptionData();
fetchAccountData();
}, [setAccountsData, open]);
const updateSubscriptionAccount = (usedAccounts: number) => {
setSubscriptionAccount((prev) => ({
...prev,
usedAccounts: usedAccounts
}));
};
const progress = (subscriptionAccount.usedAccounts / subscriptionAccount.allowedAccounts) * 100;
return (
<MainCard
title={
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h5">
<FormattedMessage id="my-accounts" />
</Typography>
<Box display="flex" alignItems="center">
<Box position="relative" display="inline-flex" mr={2}>
<CircularProgress
variant="determinate"
value={progress >= 100 ? 100 : progress}
size={40}
thickness={4}
color={progress >= 100 ? 'error' : 'primary'}
/>
<Box top={0} left={0} bottom={0} right={0} position="absolute" display="flex" alignItems="center" justifyContent="center">
<Typography variant="caption" component="div" color="textSecondary">
{`${subscriptionAccount.usedAccounts}/${subscriptionAccount.allowedAccounts}`}
</Typography>
</Box>
</Box>
<Button variant="contained" color="primary" size="small" onClick={handleOpen} className="add-account-button">
<FormattedMessage id="add-account" />
</Button>
</Box>
</Box>
}
>
<Joyride
steps={steps}
run={runTour}
continuous
showSkipButton
styles={{
options: {
zIndex: 10000
}
}}
callback={(data) => {
const { status } = data;
if (status === 'finished' || status === 'skipped') {
setRunTour(false);
}
}}
/>
{loading ? (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="200px">
<CircularProgress />
</Box>
) : error ? (
<Snackbar
open={Boolean(error)}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
autoHideDuration={2000}
onClose={handleCloseError}
>
<MuiAlert onClose={handleCloseError} severity="error" elevation={2} variant="filled">
{error}
</MuiAlert>
</Snackbar>
) : accountsData.length === 0 ? (
<Box display="flex" flexDirection="column" alignItems="center" justifyContent="center" minHeight="200px">
<Typography variant="body1" gutterBottom>
<FormattedMessage id="no-accounts-connected" />
</Typography>
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" color="primary" onClick={handleOpen} className="add-account-button">
<FormattedMessage id="add-account" />
</Button>
<Button variant="outlined" color="primary" startIcon={<HelpOutlineIcon />} onClick={handleOpenTour}>
<FormattedMessage id="help-guide" />
</Button>
</Box>
</Box>
) : (
<TableContainer>
<Table sx={{ minWidth: 650 }} aria-label="accounts table">
<TableHead>
<TableRow>
<TableCell>
<FormattedMessage id="Status" />
</TableCell>
<TableCell>
<FormattedMessage id="number" />
</TableCell>
<TableCell>
<FormattedMessage id="name" />
</TableCell>
<TableCell>
<FormattedMessage id="type" />
</TableCell>
<TableCell>
<FormattedMessage id="platform" />
</TableCell>
<TableCell>
<FormattedMessage id="balance" />
</TableCell>
<TableCell>
<FormattedMessage id="delete" />
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{accountsData.map((row) => (
<TableRow key={row.id} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell>
<Chip
label={row.status}
color={getStatusColor(row.status)}
sx={{
opacity: 0.7
}}
/>
</TableCell>
<TableCell component="th" scope="row">
{row.number}
</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>{row.account_type}</TableCell>
<TableCell>{row.platform_name}</TableCell>
<TableCell>{isNaN(Number(row.balance)) ? '-' : Number(row.balance).toFixed(2)}</TableCell>
<TableCell>
<IconButton aria-label="delete" color="error" onClick={() => handleClickOpen(row.id)}>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<AddAccountsForm handleClose={handleClose} updateSubscriptionAccount={updateSubscriptionAccount} existingAccounts={accountsData} />
</Dialog>
<ConfirmDeleteDialog open={openDelete} loadingDelete={loadingDelete} handleClose={handleCloseDelete} handleDelete={handleDelete} />
</MainCard>
);
};
export default AccountsTable;
Editor is loading...
Leave a Comment