Skip to content

Commit

Permalink
Added campaign expenses chart (#1605)
Browse files Browse the repository at this point in the history
* added campaign expenses chart

* added expense chart labels
  • Loading branch information
tongo-angelov authored Sep 25, 2023
1 parent a5efd33 commit 363a42f
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 6 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"@tryghost/content-api": "^1.11.4",
"axios": "0.21.4",
"axios-hooks": "2.7.0",
"chart.js": "^4.4.0",
"chartjs-plugin-datalabels": "^2.2.0",
"date-fns": "2.24.0",
"dompurify": "^3.0.3",
"formik": "2.2.9",
Expand All @@ -62,6 +64,7 @@
"quill-blot-formatter": "^1.0.5",
"quill-html-edit-button": "^2.2.12",
"react": "18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "18.2.0",
"react-gtm-module": "2.0.11",
"react-i18next": "^11.17.1",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/bg/expenses.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
"info": "Информация за разход"
},
"description": "Всички разходи",
"reported": "Общо отчетени",
"reported": "Общо отчетени разходи",
"donations": "Общо събрани дарения",
"uploaded-documents": "Прикачени документи",
"uploaded-files": "Прикачени файлове",
"new-files": "Нови файлове",
Expand Down
1 change: 1 addition & 0 deletions public/locales/en/expenses.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
},
"description": "All expenses",
"reported": "Total reported",
"donations": "Total donations",
"uploaded-documents": "Uploaded documents",
"uploaded-files": "Uploaded files",
"new-files": "New files",
Expand Down
27 changes: 22 additions & 5 deletions src/components/client/campaigns/CampaignDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CampaignResponse } from 'gql/campaigns'

import 'react-quill/dist/quill.bubble.css'

import { Divider, Grid, Tooltip, Typography } from '@mui/material'
import { Divider, Grid, Stack, Tooltip, Typography } from '@mui/material'
import SecurityIcon from '@mui/icons-material/Security'
import { styled } from '@mui/material/styles'

Expand All @@ -26,6 +26,7 @@ import { routes } from 'common/routes'
import { useCanEditCampaign } from 'common/hooks/campaigns'
import { moneyPublic } from 'common/util/money'
import ReceiptLongIcon from '@mui/icons-material/ReceiptLong'
import CampaignPublicExpensesChart from './CampaignPublicExpensesChart'

const ReactQuill = dynamic(() => import('react-quill'), { ssr: false })
const CampaignNewsSection = dynamic(() => import('./CampaignNewsSection'), { ssr: false })
Expand Down Expand Up @@ -144,10 +145,26 @@ export default function CampaignDetails({ campaign }: Props) {
</Typography>
</Grid>
<Grid item xs={12}>
<Typography>
<ReceiptLongIcon /> {t('expenses:reported')}:{' '}
{moneyPublic(totalExpenses || 0, campaign.currency)}
</Typography>
<Stack direction="row" gap={1} alignItems="flex-start">
<ReceiptLongIcon />
<Stack direction="row" gap={1} flexWrap="wrap">
<Typography noWrap>
{t('expenses:reported')}: {moneyPublic(totalExpenses || 0, campaign.currency)}
</Typography>
<Typography noWrap>
{t('expenses:donations')}:{' '}
{moneyPublic(campaign.summary.reachedAmount, campaign.currency)}
</Typography>
</Stack>
</Stack>
</Grid>
<Grid item xs={12}>
<CampaignPublicExpensesChart
slug={campaign.slug}
height={120}
reachedAmount={campaign.summary.reachedAmount}
currency={campaign.currency}
/>
</Grid>
<Grid item xs={12} mt={2}>
<CampaignPublicExpensesGrid slug={campaign.slug} />
Expand Down
127 changes: 127 additions & 0 deletions src/components/client/campaigns/CampaignPublicExpensesChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React from 'react'
import { observer } from 'mobx-react'
import { useTranslation } from 'next-i18next'
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Colors,
Tooltip,
Legend,
TooltipItem,
} from 'chart.js'
import { Bar } from 'react-chartjs-2'
import ChartDataLabels from 'chartjs-plugin-datalabels'

import { useCampaignApprovedExpensesList } from 'common/hooks/expenses'
import { fromMoney, moneyPublic, toMoney } from 'common/util/money'

ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Colors,
Tooltip,
Legend,
ChartDataLabels,
)

type ExpenseDataset = {
type: string
total: number
}

type Props = {
slug: string
height: number
reachedAmount: number
currency?: string
}

export default observer(function CampaignPublicExpensesChart({
slug,
height,
reachedAmount,
currency,
}: Props) {
const { t } = useTranslation('')
const { data: campaignExpenses } = useCampaignApprovedExpensesList(slug)

const expenses: ExpenseDataset[] = []

campaignExpenses?.forEach(({ type, amount }) => {
const exists = expenses.find((e) => e.type === type)
if (exists) exists.total += fromMoney(amount)
else expenses.push({ type, total: fromMoney(amount) })
})

expenses.sort((a, b) => b.total - a.total)

const options = {
indexAxis: 'y' as const,
maintainAspectRatio: false,
scales: {
x: {
stacked: true,
min: 0,
max: fromMoney(reachedAmount),
},
y: {
stacked: true,
display: false,
},
},
elements: {
bar: {
borderWidth: 1,
},
},
responsive: true,
plugins: {
legend: {
position: 'bottom' as const,
},
colors: {
enabled: true,
},
tooltip: {
callbacks: {
label: (context: TooltipItem<'bar'>) =>
` ${context.dataset.label + ':' || ''} ${moneyPublic(
toMoney(context.parsed.x),
currency,
)}`,
},
},
datalabels: {
formatter: function (value: number) {
return moneyPublic(toMoney(value), currency)
},
display: 'auto',
color: 'black',
labels: {
value: {
font: {
weight: 400,
},
},
},
},
},
}

const data = {
labels: [''],
datasets: expenses.map((expense) => {
return {
label: t('expenses:field-types.' + expense.type),
data: [expense.total],
}
}),
}

return <Bar options={options} height={height} data={data} />
})
38 changes: 38 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,13 @@ __metadata:
languageName: node
linkType: hard

"@kurkle/color@npm:^0.3.0":
version: 0.3.2
resolution: "@kurkle/color@npm:0.3.2"
checksum: 79e97b31f8f6efb28c69d373f94b0c7480226fe8ec95221f518ac998e156444a496727ce47de6d728eb5c3369288e794cba82cae34253deb0d472d3bfe080e49
languageName: node
linkType: hard

"@lexical/clipboard@npm:0.11.3, @lexical/clipboard@npm:^0.11.1":
version: 0.11.3
resolution: "@lexical/clipboard@npm:0.11.3"
Expand Down Expand Up @@ -4960,6 +4967,24 @@ __metadata:
languageName: node
linkType: hard

"chart.js@npm:^4.4.0":
version: 4.4.0
resolution: "chart.js@npm:4.4.0"
dependencies:
"@kurkle/color": ^0.3.0
checksum: 5ee2d99b78608025525b5790af17178fdaa5adc3294e082deba2718029b0496109ba124f1b08dd1e4c8a04d6e842be7f384f2cfe9a11df8d1c6fe884acece52b
languageName: node
linkType: hard

"chartjs-plugin-datalabels@npm:^2.2.0":
version: 2.2.0
resolution: "chartjs-plugin-datalabels@npm:2.2.0"
peerDependencies:
chart.js: ">=3.0.0"
checksum: 26086a908a8e88507959b7aaf798b2d9794ea95f7a5889b8bb9f6b9f3437a7e2fdf18952d3ba403b2ff78e5b70452439fb323bd0dfe76e9d7d1dae1328dacb99
languageName: node
linkType: hard

"chokidar@npm:>=3.0.0 <4.0.0":
version: 3.5.3
resolution: "chokidar@npm:3.5.3"
Expand Down Expand Up @@ -11314,6 +11339,8 @@ __metadata:
all-contributors-cli: ^6.20.0
axios: 0.21.4
axios-hooks: 2.7.0
chart.js: ^4.4.0
chartjs-plugin-datalabels: ^2.2.0
date-fns: 2.24.0
depcheck: ^1.4.3
dompurify: ^3.0.3
Expand Down Expand Up @@ -11342,6 +11369,7 @@ __metadata:
quill-blot-formatter: ^1.0.5
quill-html-edit-button: ^2.2.12
react: 18.2.0
react-chartjs-2: ^5.2.0
react-dom: 18.2.0
react-gtm-module: 2.0.11
react-i18next: ^11.17.1
Expand Down Expand Up @@ -11785,6 +11813,16 @@ __metadata:
languageName: node
linkType: hard

"react-chartjs-2@npm:^5.2.0":
version: 5.2.0
resolution: "react-chartjs-2@npm:5.2.0"
peerDependencies:
chart.js: ^4.1.1
react: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: ace702185be1450e5888a8bcd8b5fc1995067e3b11d236764a67f5567a3d7c32ff16923b8d48d3d39bda6e45135da6c044c9b43fbe8e1978f95aca9d2c0ce348
languageName: node
linkType: hard

"react-devtools-inline@npm:4.4.0":
version: 4.4.0
resolution: "react-devtools-inline@npm:4.4.0"
Expand Down

0 comments on commit 363a42f

Please sign in to comment.