Untitled

mail@pastecode.io avatar
unknown
plain_text
16 days ago
16 kB
5
Indexable
Never
'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;
Leave a Comment