Last active
August 17, 2024 09:16
-
-
Save Vetrivel-VP/ef10c6474926201f1f27184238b36112 to your computer and use it in GitHub Desktop.
Expence Tracker - Themeselection NextJs, Typescript, Mongodb
This file contains hidden or 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
Source Link : https://themeselection.com/?ref=55 | |
generator client { | |
provider = "prisma-client-js" | |
} | |
datasource db { | |
provider = "mongodb" | |
url = env("DATABASE_URL") | |
relationMode = "prisma" | |
} | |
model Budgets { | |
id String @id @default(auto()) @map("_id") @db.ObjectId | |
userId String | |
name String | |
amount String | |
icon String | |
expences Expences[] @relation("BudgetExpences") | |
createdAt DateTime @default(now()) | |
updatedAt DateTime @default(now()) | |
} | |
model Expences { | |
id String @id @default(auto()) @map("_id") @db.ObjectId | |
userId String | |
name String | |
amount String | |
budgetId String @db.ObjectId | |
budget Budgets @relation("BudgetExpences", fields: [budgetId], references: [id]) | |
createdAt DateTime @default(now()) | |
updatedAt DateTime @default(now()) | |
} | |
--------------------------------------------------------------------------------------------------------------------- | |
custom-bread-cumb.tsx | |
--------------------------------------------------------------------------------------------------------------------- | |
'use client' | |
import { Breadcrumbs, Typography } from '@mui/material' | |
import { Home } from 'lucide-react' | |
import Link from 'next/link' | |
import React from 'react' | |
interface CustomBreadCrumbProps { | |
breadCrumbPage: string | |
breadCrumbItem?: { link: string; label: string }[] | |
} | |
export const CustomBreadCrumb = ({ breadCrumbItem, breadCrumbPage }: CustomBreadCrumbProps) => { | |
return ( | |
<Breadcrumbs aria-label='breadcrumbs' separator={'›'}> | |
<Link color='primary' href='/' className='flex items-center text-purple-500'> | |
<Home className='w-4 h-4 mr-2 p-0' /> | |
Analytics | |
</Link> | |
{breadCrumbItem?.map(item => ( | |
<Link key={item.link} color='neutral' href={item.link}> | |
{item.label} | |
</Link> | |
))} | |
<Typography>{breadCrumbPage}</Typography> | |
</Breadcrumbs> | |
) | |
} | |
--------------------------------------------------------------------------------------------------------------------- | |
expense-table.tsx | |
--------------------------------------------------------------------------------------------------------------------- | |
'use client' | |
// MUI Imports | |
import Typography from '@mui/material/Typography' | |
import Card from '@mui/material/Card' | |
import Chip from '@mui/material/Chip' | |
// Third-party Imports | |
import classnames from 'classnames' | |
// Components Imports | |
import CustomAvatar from '@core/components/mui/Avatar' | |
// Styles Imports | |
import tableStyles from '@core/styles/table.module.css' | |
import { BadgeDollarSign, DollarSign, Trash, Trash2 } from 'lucide-react' | |
import { Button } from '@mui/material' | |
import { useState } from 'react' | |
import toast from 'react-hot-toast' | |
import { useRouter } from 'next/navigation' | |
import axios from 'axios' | |
export type ExpenceTableColumns = { | |
id: string | |
budgetIcon?: string | |
name?: string | |
overallBudget?: string | |
expenceName: string | |
amount: string | |
date: string | |
} | |
interface ExpenceTable { | |
rowsData: ExpenceTableColumns[] | |
isBudgetDataInclude?: boolean | |
} | |
const Table = ({ rowsData, isBudgetDataInclude }: ExpenceTable) => { | |
const router = useRouter() | |
const [isDeleting, setIsDeleting] = useState(false) | |
const handleDelete = async (id: string) => { | |
setIsDeleting(true) | |
const response = await axios.delete(`/api/expences/${id}`) | |
toast.success('Expence Removed') | |
router.refresh() | |
try { | |
} catch (error) { | |
toast.error((error as Error)?.message) | |
} finally { | |
setIsDeleting(false) | |
} | |
} | |
return ( | |
<Card> | |
<div className='overflow-x-auto'> | |
<table className={tableStyles.table}> | |
<thead> | |
<tr> | |
{isBudgetDataInclude && <th>Budget</th>} | |
<th>Expence</th> | |
<th>Amount</th> | |
<th>Date</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tbody> | |
{rowsData.map((row, index) => ( | |
<tr key={index}> | |
{isBudgetDataInclude && ( | |
<td className='!plb-1'> | |
<div className='flex items-center gap-3'> | |
<Typography variant='h3'>{row.budgetIcon}</Typography> | |
<div className='flex flex-col'> | |
<Typography color='text.primary' className='font-medium'> | |
{row.name} | |
</Typography> | |
<Typography variant='body2' className='flex items-center gap-1'> | |
<DollarSign className='w-3 h-3' /> | |
{row.overallBudget} | |
</Typography> | |
</div> | |
</div> | |
</td> | |
)} | |
<td className='!plb-1'> | |
<Typography>{row.expenceName}</Typography> | |
</td> | |
<td className='!plb-1'> | |
<div className='flex gap-2 items-center '> | |
<DollarSign className='w-4 h-4' /> | |
<Typography color='text.primary'>{row.amount}</Typography> | |
</div> | |
</td> | |
<td className='!pb-1'> | |
<Typography color='text.primary'>{row.date}</Typography> | |
</td> | |
<td className='!pb-1'> | |
<Button className='text-red-500' disabled={isDeleting} onClick={() => handleDelete(row.id)}> | |
<Trash2 /> | |
</Button> | |
</td> | |
</tr> | |
))} | |
</tbody> | |
</table> | |
</div> | |
</Card> | |
) | |
} | |
export default Table | |
--------------------------------------------------------------------------------------------------------------------- | |
Dashboard Analytics | |
--------------------------------------------------------------------------------------------------------------------- | |
import { db } from '@/libs/db' | |
import { startOfMonth, endOfMonth, subMonths, format, endOfWeek, startOfWeek } from 'date-fns' | |
// Function to format numbers as 'K' or 'M' | |
export const formatNumber = (amount: number): string => { | |
if (amount >= 1_000_000) { | |
return `${(amount / 1_000_000).toFixed(1)} M` // Millions | |
} else if (amount >= 1_000) { | |
return `${(amount / 1_000).toFixed(1)} K` // Thousands | |
} else { | |
return amount.toFixed(2) // Less than 1000 | |
} | |
} | |
export const getCurrentMonthBudgetsDetails = async () => { | |
// Determine the start and end dates of the current month | |
const now = new Date() | |
const startDate = startOfMonth(now) | |
const endDate = endOfMonth(now) | |
// Fetch all budgets with their expenses | |
const budgets = await db.budgets.findMany({ | |
include: { expences: true }, | |
where: { | |
expences: { | |
some: { | |
createdAt: { | |
gte: startDate, | |
lte: endDate | |
} | |
} | |
} | |
} | |
}) | |
// Calculate total budget amount and total expenses for the current month | |
const totalBudgetAmount = budgets.reduce((total, budget) => total + parseFloat(budget.amount), 0) | |
const totalExpenses = budgets.reduce( | |
(acc, budget) => | |
acc + | |
budget.expences.reduce((expAcc, expense) => { | |
const expenseDate = new Date(expense.createdAt) | |
// Only include expenses within the current month | |
return expenseDate >= startDate && expenseDate <= endDate ? expAcc + parseFloat(expense.amount) : expAcc | |
}, 0), | |
0 | |
) | |
const savings = totalBudgetAmount - totalExpenses | |
const percentageUsed = totalBudgetAmount > 0 ? (totalExpenses / totalBudgetAmount) * 100 : 0 | |
return { | |
totalBudgetAmount: formatNumber(totalBudgetAmount), // Apply formatting here | |
totalExpenses: formatNumber(totalExpenses), | |
savings: formatNumber(savings), // Apply formatting here | |
percentageUsed: `${percentageUsed.toFixed(2)}%` // Add percentage sign | |
} | |
} | |
// Function to get the metrics for a given month | |
const getMonthlyMetrics = async (startDate: Date, endDate: Date) => { | |
const budgets = await db.budgets.findMany({ | |
include: { expences: true }, | |
where: { | |
expences: { | |
some: { | |
createdAt: { | |
gte: startDate, | |
lte: endDate | |
} | |
} | |
} | |
} | |
}) | |
// Calculate total budget amount and total expenses | |
const totalBudgetAmount = budgets.reduce((acc, budget) => acc + parseFloat(budget.amount), 0) | |
const totalExpenses = budgets.reduce( | |
(acc, budget) => acc + budget.expences.reduce((expAcc, expense) => expAcc + parseFloat(expense.amount), 0), | |
0 | |
) | |
const savings = totalBudgetAmount - totalExpenses | |
const percentageUsed = totalBudgetAmount > 0 ? (totalExpenses / totalBudgetAmount) * 100 : 0 | |
return { | |
totalBudgetAmount, | |
totalExpenses, | |
savings, | |
percentageUsed | |
} | |
} | |
// Function to get current and previous month metrics | |
export const getComparisonMetrics = async () => { | |
const now = new Date() | |
const startOfCurrentMonth = startOfMonth(now) | |
const endOfCurrentMonth = endOfMonth(now) | |
const startOfPreviousMonth = startOfMonth(subMonths(now, 1)) | |
const endOfPreviousMonth = endOfMonth(subMonths(now, 1)) | |
const currentMonthMetrics = await getMonthlyMetrics(startOfCurrentMonth, endOfCurrentMonth) | |
const previousMonthMetrics = await getMonthlyMetrics(startOfPreviousMonth, endOfPreviousMonth) | |
return { | |
currentMonth: { | |
monthName: format(startOfCurrentMonth, 'MMMM yyyy'), | |
totalBudgetAmount: formatNumber(currentMonthMetrics.totalBudgetAmount), | |
totalExpenses: formatNumber(currentMonthMetrics.totalExpenses), | |
savings: formatNumber(currentMonthMetrics.savings), | |
percentageUsed: `${currentMonthMetrics.percentageUsed.toFixed(2)}%` | |
}, | |
previousMonth: { | |
monthName: format(startOfPreviousMonth, 'MMMM yyyy'), | |
totalBudgetAmount: formatNumber(previousMonthMetrics.totalBudgetAmount), | |
totalExpenses: formatNumber(previousMonthMetrics.totalExpenses), | |
savings: formatNumber(previousMonthMetrics.savings), | |
percentageUsed: `${previousMonthMetrics.percentageUsed.toFixed(2)}%` | |
} | |
} | |
} | |
export const getWeeklyExpenses = async (userId: string) => { | |
// Define the start date as today | |
const startDate = new Date() | |
// Calculate the end date as the end of the week from the start date | |
const endDate = endOfWeek(startDate) | |
// Query expenses within the given date range | |
const expenses = await db.expences.findMany({ | |
where: { | |
userId, | |
createdAt: { | |
gte: startOfWeek(startDate), // Start of the week | |
lte: endDate // End of the week | |
} | |
} | |
}) | |
// Initialize an array for each day of the week | |
const weeklyExpenses = Array(7).fill(0) | |
expenses.forEach(expense => { | |
const date = new Date(expense.createdAt) | |
const dayOfWeek = date.getDay() // Get day of the week (0 for Sunday, 6 for Saturday) | |
weeklyExpenses[dayOfWeek] += parseFloat(expense.amount) // Aggregate expenses by day | |
}) | |
// Fetch total budget for the user | |
const budgets = await db.budgets.findMany({ | |
where: { | |
userId | |
}, | |
select: { | |
amount: true | |
} | |
}) | |
// Calculate the total budget amount | |
const totalBudget = budgets.reduce((total, budget) => total + parseFloat(budget.amount), 0) | |
// Calculate total expenses for the week | |
const totalExpenses = weeklyExpenses.reduce((total, dailyExpense) => total + dailyExpense, 0) | |
// Calculate the percentage of expenses relative to the total budget | |
const percentage = totalBudget > 0 ? (totalExpenses / totalBudget) * 100 : 0 | |
return { | |
weeklyExpenses, | |
totalExpenses: formatNumber(totalExpenses), | |
totalBudget: formatNumber(totalBudget), | |
percentage: `${percentage.toFixed(2)}%` | |
} | |
} | |
export const getTopExpenses = async (userId: string) => { | |
// Determine the start and end dates of the current month | |
const now = new Date() | |
const startDate = startOfMonth(now) | |
const endDate = endOfMonth(now) | |
// Query expenses for the current month greater than $100 | |
const expenses = await db.expences.findMany({ | |
where: { | |
userId, | |
amount: { | |
gte: '100' // Ensure amount is in the right format for comparison | |
}, | |
createdAt: { | |
gte: startDate, | |
lte: endDate | |
} | |
}, | |
orderBy: { | |
amount: 'desc' // Sort expenses in descending order by amount | |
} | |
}) | |
return expenses.slice(0, 5) | |
} | |
--------------------------------------------------------------------------------------------------------------------- | |
Transactions.tsx | |
'use client' | |
// MUI Imports | |
import Card from '@mui/material/Card' | |
import CardHeader from '@mui/material/CardHeader' | |
import CardContent from '@mui/material/CardContent' | |
import Typography from '@mui/material/Typography' | |
import Grid from '@mui/material/Grid' | |
// Type Imports | |
import type { ThemeColor } from '@core/types' | |
// Components Imports | |
import OptionMenu from '@core/components/option-menu' | |
import CustomAvatar from '@core/components/mui/Avatar' | |
import { useRouter } from 'next/navigation' | |
import toast from 'react-hot-toast' | |
// Props type for the component | |
interface TransactionsProps { | |
currentMonth: { | |
totalBudgetAmount: string | |
totalExpenses: string | |
savings: string | |
percentageUsed: string | |
monthName: string | |
} | |
previousMonth: { | |
totalBudgetAmount: string | |
totalExpenses: string | |
savings: string | |
percentageUsed: string | |
monthName: string | |
} | |
} | |
// Component | |
const Transactions = ({ currentMonth, previousMonth }: TransactionsProps) => { | |
// Prepare data | |
const data = [ | |
{ | |
stats: `$${currentMonth.totalBudgetAmount}`, | |
title: `Budget (${currentMonth.monthName})`, | |
color: 'primary' as ThemeColor, | |
icon: 'ri-pie-chart-2-line' | |
}, | |
{ | |
stats: `$${currentMonth.totalExpenses}`, | |
title: 'Expenses', | |
color: 'warning' as ThemeColor, | |
icon: 'ri-group-line' | |
}, | |
{ | |
stats: `$${currentMonth.savings}`, | |
title: 'Savings', | |
color: 'success' as ThemeColor, | |
icon: 'ri-money-dollar-circle-line' | |
}, | |
{ | |
stats: currentMonth.percentageUsed, | |
title: 'Used Percentage', | |
color: 'info' as ThemeColor, | |
icon: 'ri-macbook-line' | |
}, | |
{ | |
stats: `$${previousMonth.totalBudgetAmount}`, | |
title: `Budget (${previousMonth.monthName})`, | |
color: 'primary' as ThemeColor, | |
icon: 'ri-pie-chart-2-line' | |
}, | |
{ | |
stats: `$${previousMonth.totalExpenses}`, | |
title: 'Expenses', | |
color: 'warning' as ThemeColor, | |
icon: 'ri-group-line' | |
}, | |
{ | |
stats: `$${previousMonth.savings}`, | |
title: 'Savings', | |
color: 'success' as ThemeColor, | |
icon: 'ri-money-dollar-circle-line' | |
}, | |
{ | |
stats: previousMonth.percentageUsed, | |
title: 'Used Percentage', | |
color: 'info' as ThemeColor, | |
icon: 'ri-macbook-line' | |
} | |
] | |
const router = useRouter() | |
const handleRefresh = async () => { | |
router.refresh() | |
toast.success('Data Reloaded') | |
} | |
return ( | |
<Card className='bs-full'> | |
<CardHeader | |
title='Budget Comparison Overview' | |
action={ | |
<OptionMenu | |
iconClassName='text-textPrimary' | |
options={[{ text: 'Refresh', menuItemProps: { onClick: handleRefresh } }]} | |
/> | |
} | |
subheader={ | |
<p className='mbs-3'> | |
<span className='font-medium text-textPrimary'> | |
Comparison between {currentMonth.monthName} and {previousMonth.monthName} Month | |
</span> | |
</p> | |
} | |
/> | |
<CardContent className='!pbs-5'> | |
<Grid container spacing={2}> | |
{data.map((item, index) => ( | |
<Grid item xs={6} md={3} key={index}> | |
<div className='flex items-center gap-3'> | |
<CustomAvatar variant='rounded' color={item.color} className='shadow-xs'> | |
<i className={item.icon}></i> | |
</CustomAvatar> | |
<div> | |
<Typography>{item.title}</Typography> | |
<Typography variant='h5'>{item.stats}</Typography> | |
</div> | |
</div> | |
</Grid> | |
))} | |
</Grid> | |
</CardContent> | |
</Card> | |
) | |
} | |
export default Transactions | |
--------------------------------------------------------------------------------------------------------------------- | |
Total Earnings | |
// MUI Imports | |
import Card from '@mui/material/Card' | |
import CardHeader from '@mui/material/CardHeader' | |
import CardContent from '@mui/material/CardContent' | |
import Avatar from '@mui/material/Avatar' | |
import LinearProgress from '@mui/material/LinearProgress' | |
import Typography from '@mui/material/Typography' | |
// Type Imports | |
import type { ThemeColor } from '@core/types' | |
// Components Imports | |
import OptionMenu from '@core/components/option-menu' | |
import { Budgets, Expences } from '@prisma/client' | |
import { formatNumber } from '../../../actions/analytics' | |
type DataType = { | |
title: string | |
imgSrc: string | |
amount: string | |
progress: number | |
subtitle: string | |
color?: ThemeColor | |
} | |
// Vars | |
const data: DataType[] = [ | |
{ | |
progress: 75, | |
title: 'Zipcar', | |
amount: '$24,895.65', | |
subtitle: 'Vuejs, React & HTML', | |
imgSrc: '/images/cards/zipcar.png' | |
}, | |
{ | |
progress: 50, | |
color: 'info', | |
title: 'Bitbank', | |
amount: '$8,650.20', | |
subtitle: 'Sketch, Figma & XD', | |
imgSrc: '/images/cards/bitbank.png' | |
}, | |
{ | |
progress: 20, | |
title: 'Aviato', | |
color: 'secondary', | |
amount: '$1,245.80', | |
subtitle: 'HTML & Angular', | |
imgSrc: '/images/cards/aviato.png' | |
} | |
] | |
interface BudgetClientProps { | |
budgets: (Budgets & { expences: Expences[] })[] | |
percentageUsed: string | |
} | |
const TotalEarning = ({ budgets, percentageUsed }: BudgetClientProps) => { | |
// Calculate the overall budget sum | |
const overallbudgetsum = budgets.reduce((total, budget) => { | |
const amount = parseFloat(budget.amount) || 0 | |
return total + amount | |
}, 0) | |
// Calculate the total expenses | |
const totalExpenses = budgets.reduce((total, budget) => { | |
const budgetExpenses = budget.expences.reduce((expTotal, expense) => { | |
const amount = parseFloat(expense.amount) || 0 | |
return expTotal + amount | |
}, 0) | |
return total + budgetExpenses | |
}, 0) | |
// Calculate the remaining overall budget | |
const remainingBudget = overallbudgetsum - totalExpenses | |
return ( | |
<Card> | |
<CardHeader title='Budgets'></CardHeader> | |
<CardContent className='flex flex-col gap-11 md:mbs-2.5'> | |
<div> | |
<div className='flex items-center'> | |
<Typography variant='h3'>${formatNumber(overallbudgetsum)}</Typography> | |
<i className='ri-arrow-up-s-line align-bottom text-success'></i> | |
<Typography component='span' color='success.main'> | |
{percentageUsed} (used) | |
</Typography> | |
</div> | |
<Typography> | |
Overall expences - ${formatNumber(totalExpenses)} | Remaining Budget : ${formatNumber(remainingBudget)} | |
</Typography> | |
</div> | |
<div className='flex flex-col gap-6'> | |
{budgets.map((item, index) => ( | |
<div key={index} className='flex items-center gap-3'> | |
{/* <Avatar src={item.icon} variant='rounded' className='bg-actionHover' /> */} | |
<Typography variant='h2' color='text.primary' className='font-medium'> | |
{item.icon} | |
</Typography> | |
<div className='flex justify-between items-center is-full flex-wrap gap-x-4 gap-y-2'> | |
<div className='flex flex-col gap-0.5'> | |
<Typography color='text.primary' className='font-medium'> | |
{item.name} | |
</Typography> | |
<Typography>Total Expences : {item.expences.length}</Typography> | |
</div> | |
<div className='flex flex-col gap-2 items-center'> | |
<Typography color='text.primary' className='font-medium'> | |
${formatNumber(parseFloat(item.amount))} | |
</Typography> | |
<BudgetProgress budget={item} /> | |
</div> | |
</div> | |
</div> | |
))} | |
</div> | |
</CardContent> | |
</Card> | |
) | |
} | |
interface BudgetCardItemProps { | |
budget: Budgets & { expences: Expences[] } | |
} | |
const BudgetProgress = ({ budget }: BudgetCardItemProps) => { | |
// Calculate total expenses | |
const totalExpenses = budget.expences.reduce((total, expence) => { | |
const amount = parseFloat(expence.amount) || 0 | |
return total + amount | |
}, 0) | |
// Calculate the budget amount and remaining amount | |
const budgetAmount = parseFloat(budget.amount) || 0 | |
const remainingAmount = budgetAmount - totalExpenses | |
// Calculate the percentage of the budget used | |
const progress = budgetAmount > 0 ? (totalExpenses / budgetAmount) * 100 : 0 | |
return <LinearProgress variant='determinate' value={progress} className='is-20 bs-1' color={'primary'} /> | |
} | |
export default TotalEarning | |
--------------------------------------------------------------------------------------------------------------------- | |
--------------------------------------------------------------------------------------------------------------------- | |
Sales Over | |
// MUI Imports | |
import Card from '@mui/material/Card' | |
import CardHeader from '@mui/material/CardHeader' | |
import CardContent from '@mui/material/CardContent' | |
import Typography from '@mui/material/Typography' | |
// Third-party Imports | |
import classnames from 'classnames' | |
// Type Imports | |
import type { ThemeColor } from '@core/types' | |
// Components Imports | |
import OptionMenu from '@core/components/option-menu' | |
import CustomAvatar from '@core/components/mui/Avatar' | |
import { Expences } from '@prisma/client' | |
import { format } from 'date-fns' | |
import { formatNumber } from '../../../actions/analytics' | |
import { DollarSign } from 'lucide-react' | |
// type DataType = { | |
// avatarLabel: string | |
// avatarColor?: ThemeColor | |
// title: string | |
// subtitle: string | |
// sales: string | |
// trend: 'up' | 'down' | |
// trendPercentage: string | |
// } | |
// // Vars | |
// const data: DataType[] = [ | |
// { | |
// avatarLabel: 'US', | |
// avatarColor: 'success', | |
// title: '$8,656k', | |
// subtitle: 'United states of america', | |
// sales: '894k', | |
// trend: 'up', | |
// trendPercentage: '25.8%' | |
// }, | |
// { | |
// avatarLabel: 'UK', | |
// avatarColor: 'error', | |
// title: '$2,415k', | |
// subtitle: 'United kingdom', | |
// sales: '645k', | |
// trend: 'down', | |
// trendPercentage: '6.2%' | |
// }, | |
// { | |
// avatarLabel: 'IN', | |
// avatarColor: 'warning', | |
// title: '$865k', | |
// subtitle: 'India', | |
// sales: '148k', | |
// trend: 'up', | |
// trendPercentage: '12.4%' | |
// }, | |
// { | |
// avatarLabel: 'JA', | |
// avatarColor: 'secondary', | |
// title: '$745k', | |
// subtitle: 'Japan', | |
// sales: '86k', | |
// trend: 'down', | |
// trendPercentage: '11.9%' | |
// }, | |
// { | |
// avatarLabel: 'KO', | |
// avatarColor: 'error', | |
// title: '$45k', | |
// subtitle: 'Korea', | |
// sales: '42k', | |
// trend: 'up', | |
// trendPercentage: '16.2%' | |
// } | |
// ] | |
interface PageClientProps { | |
expences: Expences[] | [] | |
} | |
const SalesByCountries = ({ expences }: PageClientProps) => { | |
return ( | |
<Card> | |
<CardHeader title='Recent Expences' /> | |
<CardContent className='flex flex-col gap-[0.875rem]'> | |
{expences.map((item, index) => ( | |
<div key={index} className='flex items-center gap-4'> | |
<CustomAvatar skin='light' color={'primary'}> | |
<DollarSign /> | |
</CustomAvatar> | |
<div className='flex items-center justify-between is-full flex-wrap gap-x-4 gap-y-2'> | |
<div className='flex flex-col gap-1'> | |
<div className='flex items-center gap-1'> | |
<Typography color='text.primary' className='font-medium'> | |
{item.name} | |
</Typography> | |
{/* <div className={'flex items-center gap-1'}> | |
<i | |
className={classnames( | |
item.trend === 'up' ? 'ri-arrow-up-s-line' : 'ri-arrow-down-s-line', | |
item.trend === 'up' ? 'text-success' : 'text-error' | |
)} | |
></i> | |
<Typography color={item.trend === 'up' ? 'success.main' : 'error.main'}> | |
{item.trendPercentage} | |
</Typography> | |
</div> */} | |
</div> | |
<Typography variant='body2' color='text.disabled'> | |
{format(new Date(item.createdAt), 'MMMM dd, yyyy')} | |
</Typography> | |
</div> | |
<div className='flex flex-col gap-1'> | |
<Typography variant='body2' color='text.primary' className='font-medium'> | |
${formatNumber(parseFloat(item.amount))} | |
</Typography> | |
</div> | |
</div> | |
</div> | |
))} | |
</CardContent> | |
</Card> | |
) | |
} | |
export default SalesByCountries |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment