diff --git a/src/components/dashboard/FirstSteps/index.tsx b/src/components/dashboard/FirstSteps/index.tsx new file mode 100644 index 0000000000..ec1473b7be --- /dev/null +++ b/src/components/dashboard/FirstSteps/index.tsx @@ -0,0 +1,77 @@ +import useBalances from '@/hooks/useBalances' +import { useAppSelector } from '@/store' +import { selectOutgoingTransactions } from '@/store/txHistorySlice' +import { type ReactNode } from 'react' +import { Card, WidgetBody, WidgetContainer } from '@/components/dashboard/styled' +import { LinearProgress, List, ListItem, ListItemIcon, Typography } from '@mui/material' +import CircleOutlinedIcon from '@mui/icons-material/CircleOutlined' +import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded' + +const StatusItem = ({ children, completed = false }: { children: ReactNode; completed?: boolean }) => { + return ( + + theme.palette.primary.main }}> + {completed ? : } + + {children} + + ) +} + +type StatusProgressItems = Array<{ + name: string + completed: boolean +}> + +const StatusProgress = ({ items }: { items: StatusProgressItems }) => { + const totalNumberOfItems = items.length + const completedItems = items.filter((item) => item.completed) + const progress = Math.ceil(completedItems.length / totalNumberOfItems) * 100 + + return ( + <> + + {items.map(({ name, completed }) => { + return ( + + {name} + + ) + })} + + + + {progress}% completed + + + ) +} + +const FirstSteps = () => { + const { balances } = useBalances() + const outgoingTransactions = useAppSelector(selectOutgoingTransactions) + + const hasNonZeroBalance = balances && (balances.items.length > 1 || BigInt(balances.items[0]?.balance || 0) > 0) + const hasOutgoingTransactions = !!outgoingTransactions && outgoingTransactions.length > 0 + + const items = [ + { name: 'Add funds', completed: hasNonZeroBalance }, + { name: 'Create your first transaction', completed: hasOutgoingTransactions }, + { name: 'Safe is ready', completed: hasNonZeroBalance && hasOutgoingTransactions }, + ] + + return ( + + + + + First steps + + + + + + ) +} + +export default FirstSteps diff --git a/src/store/txHistorySlice.ts b/src/store/txHistorySlice.ts index d9717d0a3b..bf7d0fb8fe 100644 --- a/src/store/txHistorySlice.ts +++ b/src/store/txHistorySlice.ts @@ -1,6 +1,7 @@ import type { listenerMiddlewareInstance } from '@/store' +import { createSelector } from '@reduxjs/toolkit' import type { TransactionListPage } from '@safe-global/safe-gateway-typescript-sdk' -import { isTransactionListItem } from '@/utils/transaction-guards' +import { isCreationTxInfo, isIncomingTransfer, isTransactionListItem } from '@/utils/transaction-guards' import { txDispatch, TxEvent } from '@/services/tx/txEvents' import { selectPendingTxs } from './pendingTxsSlice' import { makeLoadableSlice } from './common' @@ -10,6 +11,12 @@ const { slice, selector } = makeLoadableSlice('txHistory', undefined as Transact export const txHistorySlice = slice export const selectTxHistory = selector +export const selectOutgoingTransactions = createSelector(selectTxHistory, (txHistory) => { + return txHistory.data?.results.filter(isTransactionListItem).filter((tx) => { + return !isIncomingTransfer(tx.transaction.txInfo) && !isCreationTxInfo(tx.transaction.txInfo) + }) +}) + export const txHistoryListener = (listenerMiddleware: typeof listenerMiddlewareInstance) => { listenerMiddleware.startListening({ actionCreator: txHistorySlice.actions.set, diff --git a/src/utils/transaction-guards.ts b/src/utils/transaction-guards.ts index 06b1591131..817dca0591 100644 --- a/src/utils/transaction-guards.ts +++ b/src/utils/transaction-guards.ts @@ -100,6 +100,10 @@ export const isOutgoingTransfer = (txInfo: TransactionInfo): boolean => { return isTransferTxInfo(txInfo) && txInfo.direction.toUpperCase() === TransferDirection.OUTGOING } +export const isIncomingTransfer = (txInfo: TransactionInfo): boolean => { + return isTransferTxInfo(txInfo) && txInfo.direction.toUpperCase() === TransferDirection.INCOMING +} + // TransactionListItem type guards export const isLabelListItem = (value: TransactionListItem): value is Label => { return value.type === TransactionListItemType.LABEL