Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AMB-353] feat: detailed and recent transactions #87

Merged
merged 4 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading