-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #48 from arifshariati/banking
feat(banking): recent transactions
- Loading branch information
Showing
10 changed files
with
519 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
'use client'; | ||
|
||
import { useRouter, useSearchParams } from 'next/navigation'; | ||
import { Landmark } from 'lucide-react'; | ||
import { cn } from '@monor/utils/tailwind/cn'; | ||
import { formatAmount, formUrlQuery, getAccountTypeColors } from '../lib/utils'; | ||
import { Account, AccountTypes } from '../types/account'; | ||
|
||
type BankInfoProps = { | ||
account: Account; | ||
appwriteItemId?: string; | ||
type: 'full' | 'card'; | ||
}; | ||
|
||
const BankInfo = ({ account, appwriteItemId, type }: BankInfoProps) => { | ||
const router = useRouter(); | ||
const searchParams = useSearchParams(); | ||
|
||
const isActive = appwriteItemId === account?.appwriteItemId; | ||
|
||
const handleBankChange = () => { | ||
const newUrl = formUrlQuery({ | ||
params: searchParams.toString(), | ||
key: 'id', | ||
value: account?.appwriteItemId, | ||
}); | ||
router.push(newUrl, { scroll: false }); | ||
}; | ||
|
||
const colors = getAccountTypeColors(account?.type as AccountTypes); | ||
|
||
return ( | ||
<div | ||
onClick={handleBankChange} | ||
className="flex px-4 py-2 gap-4 bg-blue-50 rounded-lg" | ||
> | ||
<div className="flex items-center justify-center "> | ||
<div className="flex items-center justify-center bg-blue-100 h-[50px] w-[50px] rounded-lg"> | ||
<Landmark /> | ||
</div> | ||
</div> | ||
|
||
<div className="flex flex-1 items-center justify-between"> | ||
<div className="flex flex-col"> | ||
<h2 className="text-xl font-semibold">{account.name}</h2> | ||
{type === 'full' && ( | ||
<p className="italic text-sm">{account.subtype}</p> | ||
)} | ||
</div> | ||
|
||
<p className="text-2xl font-semibold"> | ||
{formatAmount(account.currentBalance)} | ||
</p> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default BankInfo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
'use client'; | ||
import { Account } from '../types/account'; | ||
import { useRouter, useSearchParams } from 'next/navigation'; | ||
import { formUrlQuery } from '../lib/utils'; | ||
import { cn } from '@monor/utils/tailwind/cn'; | ||
|
||
type BankTabItemProps = { account: Account; appwriteItemId?: string }; | ||
|
||
const BankTabItem = ({ account, appwriteItemId }: BankTabItemProps) => { | ||
const searchParams = useSearchParams(); | ||
const router = useRouter(); | ||
const isActive = appwriteItemId === account.appwriteItemId; | ||
|
||
const handleTabChange = () => { | ||
const newUrl = formUrlQuery({ | ||
params: searchParams.toString(), | ||
key: 'id', | ||
value: account.appwriteItemId, | ||
}); | ||
router.push(newUrl, { scroll: false }); | ||
}; | ||
|
||
return <div onClick={handleTabChange}>{account.name}</div>; | ||
}; | ||
|
||
export default BankTabItem; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
'use client'; | ||
import { useRouter, useSearchParams } from 'next/navigation'; | ||
import { ChevronLeft, ChevronRight } from 'lucide-react'; | ||
import { Button } from '@monor/ui/shadcn/button'; | ||
import { formUrlQuery } from '../lib/utils'; | ||
|
||
type PaginationProps = { page: number; totalPages: number }; | ||
|
||
const Pagination = ({ page, totalPages }: PaginationProps) => { | ||
const router = useRouter(); | ||
const searchParams = useSearchParams(); | ||
|
||
const handleNavigation = (type: 'prev' | 'next') => { | ||
const pageNumber = type === 'prev' ? page - 1 : page + 1; | ||
|
||
const newUrl = formUrlQuery({ | ||
params: searchParams.toString(), | ||
key: 'page', | ||
value: pageNumber.toString(), | ||
}); | ||
|
||
router.push(newUrl, { scroll: false }); | ||
}; | ||
|
||
return ( | ||
<div className="flex justify-between gap-3"> | ||
<Button | ||
size="lg" | ||
variant="ghost" | ||
className="p-0 hover:bg-transparent" | ||
onClick={() => handleNavigation('prev')} | ||
disabled={Number(page) <= 1} | ||
> | ||
<ChevronLeft /> | ||
Prev | ||
</Button> | ||
<p className="text-14 flex items-center px-2"> | ||
{page} / {totalPages} | ||
</p> | ||
<Button | ||
size="lg" | ||
variant="ghost" | ||
className="p-0 hover:bg-transparent" | ||
onClick={() => handleNavigation('next')} | ||
disabled={Number(page) >= totalPages} | ||
> | ||
Next | ||
<ChevronRight /> | ||
</Button> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Pagination; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import Link from 'next/link'; | ||
import { | ||
Tabs, | ||
TabsContent, | ||
TabsList, | ||
TabsTrigger, | ||
} from '@monor/ui/shadcn/tabs'; | ||
import { Account } from '../types/account'; | ||
import { Transaction } from '../types/transaction'; | ||
import BankTabItem from './bank-tab-item'; | ||
import BankInfo from './bank-info'; | ||
import TransactionsTable from './transactions-table'; | ||
import Pagination from './pagination'; | ||
import { Button } from '@monor/ui/shadcn/button'; | ||
|
||
type RecentTransactionsProps = { | ||
accounts: Account[]; | ||
transactions: Transaction[]; | ||
appwriteItemId: string; | ||
page: number; | ||
}; | ||
|
||
const RecentTransactions = ({ | ||
accounts, | ||
transactions = [], | ||
appwriteItemId, | ||
page = 1, | ||
}: RecentTransactionsProps) => { | ||
const rowsPerPage = 10; | ||
const totalPages = Math.ceil(transactions.length / rowsPerPage); | ||
|
||
const indexOfLastTransaction = page * rowsPerPage; | ||
const indexOfFirstTransaction = indexOfLastTransaction - rowsPerPage; | ||
|
||
const currentTransactions = transactions.slice( | ||
indexOfFirstTransaction, | ||
indexOfLastTransaction | ||
); | ||
|
||
return ( | ||
<section className="flex w-full flex-col gap-6"> | ||
<header className="flex items-center justify-between"> | ||
<h2 className="text-20 md:text-24 font-semibold text-gray-900"> | ||
Recent transactions | ||
</h2> | ||
<Link href={`/transaction-history/?id=${appwriteItemId}`}> | ||
<Button variant={'outline'}>View All</Button> | ||
</Link> | ||
</header> | ||
|
||
<Tabs defaultValue={appwriteItemId} className="w-auto"> | ||
<TabsList className="flex items-center justify-center"> | ||
{accounts.map((account: Account) => ( | ||
<TabsTrigger key={account.id} value={account.appwriteItemId}> | ||
<BankTabItem | ||
key={account.id} | ||
account={account} | ||
appwriteItemId={appwriteItemId} | ||
/> | ||
</TabsTrigger> | ||
))} | ||
</TabsList> | ||
|
||
{accounts.map((account: Account) => ( | ||
<TabsContent | ||
value={account.appwriteItemId} | ||
key={account.id} | ||
className="space-y-2" | ||
> | ||
<BankInfo | ||
account={account} | ||
appwriteItemId={appwriteItemId} | ||
type="full" | ||
/> | ||
|
||
<TransactionsTable transactions={currentTransactions} /> | ||
|
||
{totalPages > 1 && ( | ||
<div className="my-4 w-full"> | ||
<Pagination totalPages={totalPages} page={page} /> | ||
</div> | ||
)} | ||
</TabsContent> | ||
))} | ||
</Tabs> | ||
</section> | ||
); | ||
}; | ||
|
||
export default RecentTransactions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { cn } from '@monor/utils/tailwind/cn'; | ||
import { | ||
Table, | ||
TableBody, | ||
TableCell, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
} from '@monor/ui/shadcn/table'; | ||
import { Transaction } from '../types/transaction'; | ||
import { transactionCategoryStyles } from '../constants/transaction-table'; | ||
import { | ||
formatAmount, | ||
formatDateTime, | ||
getTransactionStatus, | ||
removeSpecialCharacters, | ||
} from '../lib/utils'; | ||
|
||
const CategoryBadge = ({ category }: { category: string }) => { | ||
const { borderColor, backgroundColor, textColor, chipBackgroundColor } = | ||
transactionCategoryStyles[ | ||
category as keyof typeof transactionCategoryStyles | ||
] || transactionCategoryStyles.default; | ||
|
||
return ( | ||
<div | ||
className={cn( | ||
'flex items-center justify-center truncate w-fit gap-1 rounded-2xl border-[1.5px] py-[2px] pl-1.5 pr-2', | ||
borderColor, | ||
chipBackgroundColor | ||
)} | ||
> | ||
<div className={cn('size-2 rounded-full', backgroundColor)} /> | ||
<p className={cn('text-[12px] font-medium', textColor)}>{category}</p> | ||
</div> | ||
); | ||
}; | ||
|
||
type TransactionTablesProps = { transactions: Transaction[] }; | ||
|
||
const TransactionTables = ({ transactions }: TransactionTablesProps) => { | ||
return ( | ||
<Table> | ||
<TableHeader className="bg-[#f9fafb]"> | ||
<TableRow> | ||
<TableHead className="px-2">Transaction</TableHead> | ||
<TableHead className="px-2">Amount</TableHead> | ||
<TableHead className="px-2">Status</TableHead> | ||
<TableHead className="px-2">Date</TableHead> | ||
<TableHead className="px-2 max-md:hidden">Channel</TableHead> | ||
<TableHead className="px-2 max-md:hidden">Category</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{transactions.map((t: Transaction) => { | ||
const status = getTransactionStatus(new Date(t.date)); | ||
const amount = formatAmount(t.amount); | ||
|
||
const isDebit = t.type === 'debit'; | ||
const isCredit = t.type === 'credit'; | ||
|
||
return ( | ||
<TableRow | ||
key={t.id} | ||
className={`${ | ||
isDebit || amount[0] === '-' ? 'bg-[#FFFBFA]' : 'bg-[#F6FEF9]' | ||
} !over:bg-none !border-b-DEFAULT`} | ||
> | ||
<TableCell className="max-w-[250px] pl-2 pr-10"> | ||
<div className="flex items-center gap-3"> | ||
<h1 className="text-14 truncate font-semibold text-[#344054]"> | ||
{removeSpecialCharacters(t.name)} | ||
</h1> | ||
</div> | ||
</TableCell> | ||
|
||
<TableCell | ||
className={`pl-2 pr-10 font-semibold ${ | ||
isDebit || amount[0] === '-' | ||
? 'text-[#f04438]' | ||
: 'text-[#039855]' | ||
}`} | ||
> | ||
{isDebit ? `-${amount}` : isCredit ? amount : amount} | ||
</TableCell> | ||
|
||
<TableCell className="pl-2 pr-10"> | ||
<CategoryBadge category={status} /> | ||
</TableCell> | ||
|
||
<TableCell className="min-w-32 pl-2 pr-10"> | ||
{formatDateTime(new Date(t.date)).dateTime} | ||
</TableCell> | ||
|
||
<TableCell className="pl-2 pr-10 capitalize min-w-24"> | ||
{t.paymentChannel} | ||
</TableCell> | ||
|
||
<TableCell className="pl-2 pr-10 max-md:hidden"> | ||
<CategoryBadge category={t.category} /> | ||
</TableCell> | ||
</TableRow> | ||
); | ||
})} | ||
</TableBody> | ||
</Table> | ||
); | ||
}; | ||
|
||
export default TransactionTables; |
Oops, something went wrong.