Skip to content

Commit

Permalink
feat: detailed and recent transactions (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
secondl1ght authored Oct 7, 2024
1 parent 898631f commit 79fac2f
Show file tree
Hide file tree
Showing 10 changed files with 534 additions and 83 deletions.
17 changes: 16 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"recent": "Recent Contacts",
"sell": "Sell",
"tether-explainer": "Tether (USDT) is a stablecoin, a type of cryptocurrency designed to maintain a stable value relative to a fiat currency, in this case, the United States dollar (USD). It is issued by Tether Limited, a company founded in 2014, and is pegged to the value of the US dollar.",
"transactions": "Recent Transactions",
"warning": "In order to send USD you need to add Bitcoin to your wallet.",
"warning-title": "Add Bitcoin!",
"what-asset": "What is {asset}?"
Expand All @@ -27,8 +28,22 @@
"Transactions": {
"ago": "ago",
"all": "All",
"amount": "Amount",
"asset": "Asset",
"blinded": "Copy Blinded URL",
"copy-id": "Copy ID",
"copy-tx": "Copy Transaction ID",
"date": "Date",
"details": "Transaction Details",
"fees": "Fees",
"no-results": "No results.",
"pending": "Pending"
"paid": "Paid",
"pending": "Pending",
"received": "Received",
"sent": "Sent",
"status": "Status",
"time": "Time",
"unblinded": "Copy Unblinded URL"
},
"Vault": {
"lock": "Lock",
Expand Down
19 changes: 17 additions & 2 deletions messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"recent": "Contactos Recientes",
"sell": "Vender",
"tether-explainer": "Tether (USDT) es una stablecoin, un tipo de criptomoneda diseñada para mantener un valor estable en relación con una moneda fiduciaria, en este caso, el dólar estadounidense (USD). Es emitida por Tether Limited, una compañía fundada en 2014, y está vinculada al valor del dólar estadounidense.",
"transactions": "Transacciones Recientes",
"warning": "Para enviar USD tienes que tener Bitcoin en tu billetera.",
"warning-title": "Agrega Bitcoin!",
"what-asset": "Qué es {asset}?"
Expand All @@ -25,10 +26,24 @@
"unlock": "Desbloquear para Desencriptar"
},
"Transactions": {
"ago": "ago",
"ago": "Hace",
"all": "Todas",
"amount": "Cantidad",
"asset": "Activo",
"blinded": "Copiar URL Privado",
"copy-id": "Copiar ID",
"copy-tx": "Copiar ID de la Transacción",
"date": "Fecha",
"details": "Detalles de la Transacción",
"fees": "Comisiónes",
"no-results": "Sin resultados.",
"pending": "Procesando"
"paid": "Pagado",
"pending": "Procesando",
"received": "Recivido",
"sent": "Envíado",
"status": "Estado",
"time": "Tiempo",
"unblinded": "Copiar URL Público"
},
"Vault": {
"lock": "Bloquear",
Expand Down
5 changes: 5 additions & 0 deletions src/app/(app)/(layout)/transactions/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TransactionDetail } from '@/views/transactions/TransactionDetail';

export default function Page({ params }: { params: { id: string } }) {
return <TransactionDetail id={params.id} />;
}
5 changes: 4 additions & 1 deletion src/utils/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export const ROUTES = {
home: '/settings',
twofa: '/settings/2fa',
},
transactions: { home: '/transactions' },
transactions: {
home: '/transactions',
tx: (id: string) => `/transactions/${id}`,
},
swaps: {
home: '/swaps',
},
Expand Down
2 changes: 2 additions & 0 deletions src/views/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ROUTES } from '@/utils/routes';
import { WalletInfo } from '../wallet/WalletInfo';
import { BancoCode } from './BancoCode';
import { RecentContacts } from './RecentContacts';
import { RecentTransactions } from './RecentTransactions';

export type DashboardView = 'default' | 'assets' | 'asset';

Expand Down Expand Up @@ -108,6 +109,7 @@ export const Dashboard = () => {
<BancoCode id={value} />
<WalletInfo id={value} view={view} setView={setView} />
<RecentContacts id={value} />
<RecentTransactions id={value} />
</>
) : (
<WalletInfo id={value} view={view} setView={setView} />
Expand Down
110 changes: 110 additions & 0 deletions src/views/dashboard/RecentTransactions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { sortBy } from 'lodash';
import Link from 'next/link';
import { useTranslations } from 'next-intl';
import { FC, useMemo } from 'react';

import { Skeleton } from '@/components/ui/skeleton';
import { useToast } from '@/components/ui/use-toast';
import { useGetWalletQuery } from '@/graphql/queries/__generated__/wallet.generated';
import { handleApolloError } from '@/utils/error';
import { cryptoToUsd } from '@/utils/fiat';
import { ROUTES } from '@/utils/routes';

import { Transaction } from '../transactions/Transaction';
import { TransactionEntry } from '../transactions/Transactions';

export const RecentTransactions: FC<{ id: string }> = ({ id }) => {
const t = useTranslations('App');
const { toast } = useToast();

const { data, loading, error } = useGetWalletQuery({
variables: { id },
onError: err => {
const messages = handleApolloError(err);

toast({
variant: 'destructive',
title: 'Error getting transactions.',
description: messages.join(', '),
});
},
});

const transactions = useMemo(() => {
if (loading || error) return [];
if (!data?.wallets.find_one.accounts.length) return [];

const { accounts } = data.wallets.find_one;

const transactions: TransactionEntry[] = [];

accounts.forEach(a => {
if (!a.liquid) return;

a.liquid.transactions.forEach(t => {
transactions.push({
id: t.id,
balance: t.balance,
formatted_balance: cryptoToUsd(
t.balance,
t.asset_info.precision,
t.asset_info.ticker,
t.fiat_info.fiat_to_btc
),
date: t.date,
ticker: t.asset_info.ticker,
precision: t.asset_info.precision,
});
});
});

const sorted = sortBy(transactions, t =>
t.date ? new Date(t.date) : new Date()
).reverse();

return sorted.slice(0, 5);
}, [data, loading, error]);

return (
<div>
<div className="mb-4 flex w-full justify-between space-x-2 lg:mb-6">
<p className="text-2xl font-semibold">{t('Dashboard.transactions')}</p>

<Link
href={ROUTES.transactions.home}
className="font-medium text-primary transition-colors hover:text-primary-hover"
>
{t('view-all')}
</Link>
</div>

<div className="w-full max-w-[calc(100dvw-32px)] space-y-2">
{loading ? (
Array.from({ length: 5 }).map((_, i) => (
<Skeleton key={i} className="h-[52px] w-full rounded-xl" />
))
) : (
<>
{transactions.length ? (
transactions.map(t => (
<Transaction
key={t.id}
id={t.id}
balance={t.balance}
precision={t.precision}
date={t.date}
formatted_balance={t.formatted_balance}
ticker={t.ticker}
/>
))
) : (
<p className="font-semibold">
{t('Wallet.Transactions.no-results')}
</p>
)}
</>
)}
</div>
</div>
);
};
79 changes: 79 additions & 0 deletions src/views/transactions/Transaction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { format, formatDistanceToNowStrict } from 'date-fns';
import { es } from 'date-fns/locale';
import { ArrowDown, ArrowUp } from 'lucide-react';
import Link from 'next/link';
import { useLocale, useTranslations } from 'next-intl';
import { FC } from 'react';

import { cn } from '@/utils/cn';
import { numberWithPrecisionAndDecimals } from '@/utils/numbers';
import { ROUTES } from '@/utils/routes';

export const Transaction: FC<{
id: string;
balance: string;
precision: number;
date: string | undefined | null;
formatted_balance: string;
ticker: string;
}> = ({ id, balance, precision, date, formatted_balance, ticker }) => {
const t = useTranslations();
const locale = useLocale();

const balanceNum = Number(balance);

const formatted = numberWithPrecisionAndDecimals(
parseFloat(balance),
precision
);

return (
<Link href={ROUTES.transactions.tx(id)} className="block">
<div className="group flex w-full items-center justify-between space-x-2 overflow-x-auto whitespace-nowrap rounded-xl bg-slate-100 px-2 py-1 transition-colors dark:bg-neutral-900 dark:hover:bg-neutral-800">
<div className="flex items-center space-x-2">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-white transition-colors group-hover:bg-slate-100 dark:bg-neutral-800 group-hover:dark:bg-neutral-800">
{balanceNum < 0 ? (
<ArrowUp size={24} />
) : (
<ArrowDown size={24} className="text-green-400" />
)}
</div>

{date ? (
<div>
<p className="font-medium">
{locale === 'es'
? t('App.Wallet.Transactions.ago') +
' ' +
formatDistanceToNowStrict(date, {
locale: es,
})
: formatDistanceToNowStrict(date) +
' ' +
t('App.Wallet.Transactions.ago')}
</p>

<p className="text-sm text-slate-600 dark:text-neutral-400">
{format(date, 'MMM dd, yyyy')}
</p>
</div>
) : (
<p>{t('App.Wallet.Transactions.pending')}</p>
)}
</div>

<div className="text-right">
<p className={cn('font-medium', balanceNum > 0 && 'text-green-400')}>
{formatted_balance.includes('-')
? '-' + formatted_balance.replaceAll('-', '')
: '+' + formatted_balance}
</p>

<p className="text-sm text-slate-600 dark:text-neutral-400">
{formatted.includes('-') ? formatted : '+' + formatted} {ticker}
</p>
</div>
</div>
</Link>
);
};
Loading

0 comments on commit 79fac2f

Please sign in to comment.