-
-
Notifications
You must be signed in to change notification settings - Fork 637
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: migrate finance components (#2747)
- Loading branch information
1 parent
133eaa3
commit f7c6ea6
Showing
18 changed files
with
1,421 additions
and
594 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; | ||
import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; | ||
|
||
import Button from '../buttons/Button'; | ||
import Heading from '../typography/Heading'; | ||
import Paragraph from '../typography/Paragraph'; | ||
|
||
/** | ||
* @description Component representing the AsyncAPI Financial Summary. | ||
*/ | ||
export default function AsyncAPISummary() { | ||
return ( | ||
<div className='px-4 sm:px-6 lg:px-8'> | ||
<div className='mx-4 my-8 grid lg:grid-cols-9 lg:gap-8 lg:text-center'> | ||
<div className='col-span-5 col-start-3'> | ||
<Heading level={HeadingLevel.h2} className='m-3 text-center text-5xl'> | ||
AsyncAPI Financial Summary | ||
</Heading> | ||
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-1 max-w-4xl text-darkGunMetal'> | ||
To help improve the current state of Event-Driven Architectures and their tooling, you can show your support | ||
for the AsyncAPI Initiative by making a financial contribution. We offer three donation options:{' '} | ||
<strong>Open Collective, GitHub Sponsors, and Linux Foundation Crowdfunding</strong>. Our expenses are | ||
managed through Open Collective and GitHub Sponsors, while Linux Foundation Crowdfunding operates | ||
separately. | ||
</Paragraph> | ||
</div> | ||
</div> | ||
<div className='mb-20 flex justify-center'> | ||
<Button | ||
text='Become a Sponsor' | ||
href='https://opencollective.com/asyncapi#category-CONTRIBUTE' | ||
target='_blank' | ||
/> | ||
</div> | ||
<div className='mt-4 text-center text-sm'> | ||
<Heading level={HeadingLevel.h1} typeStyle={HeadingTypeStyle.md} className='4xl'> | ||
Ways to Support Us? | ||
</Heading> | ||
</div> | ||
<div className='max-width my-4 text-center text-base text-darkGunMetal'> | ||
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='my-4'> | ||
The easiest way to support AsyncAPI is by becoming a financial sponsor. While{' '} | ||
<br className='hidden lg:inline-block' /> | ||
there are alternative options, they may involve greater effort. Contribute{' '} | ||
<br className='hidden lg:inline-block' /> | ||
monetarily using the following channels. | ||
</Paragraph> | ||
</div> | ||
|
||
<div className='text-center'> | ||
<a href='https://opencollective.com/asyncapi' target='_blank'> | ||
<img | ||
className='mx-4 inline size-10 transition-transform hover:scale-110 active:scale-90' | ||
src='/img/logos/OpenCollective.svg' | ||
alt='Open Collective' | ||
/> | ||
</a> | ||
<a | ||
href='https://crowdfunding.lfx.linuxfoundation.org/projects/445898e9-42a2-4965-9e0a-c2a714f381bc' | ||
target='_blank' | ||
> | ||
<img | ||
className='mx-4 inline size-10 transition-transform hover:scale-110 active:scale-90' | ||
src='/img/logos/LFX.svg' | ||
alt='Linux Foundation' | ||
/> | ||
</a> | ||
<a href='https://github.com/sponsors/asyncapi' target='_blank'> | ||
<img | ||
className='mx-4 inline size-10 transition-transform hover:scale-110 active:scale-90' | ||
src='/img/logos/github-black.svg' | ||
alt='Github' | ||
/> | ||
</a> | ||
</div> | ||
</div> | ||
); | ||
} |
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,136 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { Bar, BarChart, CartesianGrid, Legend, Tooltip, YAxis } from 'recharts'; | ||
|
||
import type { ExpenseItem, ExpensesLinkItem } from '@/types/FinancialSummary/BarChartComponent'; | ||
|
||
import ExpensesData from '../../config/finance/json-data/2024/Expenses.json'; | ||
import ExpensesLinkData from '../../config/finance/json-data/2024/ExpensesLink.json'; | ||
import { getUniqueCategories } from '../../utils/getUniqueCategories'; | ||
import CustomTooltip from './CustomTooltip'; | ||
import ExpensesCard from './ExpensesCard'; | ||
|
||
/** | ||
* @description BarChartComponent component displays a bar chart for expense analysis. | ||
*/ | ||
export default function BarChartComponent() { | ||
// Setting up state variables using useState hook | ||
const [selectedCategory, setSelectedCategory] = useState<string>('All Categories'); | ||
const [selectedMonth, setSelectedMonth] = useState<string>('All Months'); | ||
const [windowWidth, setWindowWidth] = useState<number>(0); | ||
|
||
// Extracting unique categories and months from the data | ||
const categories: string[] = getUniqueCategories(); | ||
const months: string[] = Object.keys(ExpensesData); | ||
|
||
// Effect hook to update windowWidth state on resize | ||
useEffect(() => { | ||
const handleResize = () => { | ||
setWindowWidth(window.innerWidth); | ||
}; | ||
|
||
// Initial setup and event listener | ||
handleResize(); | ||
window.addEventListener('resize', handleResize); | ||
|
||
// Cleanup function to remove event listener | ||
return () => { | ||
window.removeEventListener('resize', handleResize); | ||
}; | ||
}, []); | ||
|
||
// Filtering data based on selected month and category | ||
const filteredData: ExpenseItem[] = Object.entries(ExpensesData).flatMap(([month, entries]) => selectedMonth === 'All Months' || selectedMonth === month | ||
? entries.filter((entry) => selectedCategory === 'All Categories' || entry.Category === selectedCategory) | ||
: []); | ||
|
||
// Calculating total amount of filtered data | ||
const totalAmount: number = filteredData.reduce((total, entry) => total + parseFloat(entry.Amount), 0); | ||
|
||
// Calculating total amount per category | ||
const categoryAmounts: { [category: string]: number } = {}; | ||
|
||
filteredData.forEach((entry) => { | ||
if (categoryAmounts[entry.Category]) { | ||
categoryAmounts[entry.Category] += parseFloat(entry.Amount); | ||
} else { | ||
categoryAmounts[entry.Category] = parseFloat(entry.Amount); | ||
} | ||
}); | ||
|
||
// Formatting data for the chart | ||
const chartData: { Category: string; Amount: number }[] = Object.keys(categoryAmounts).map((category) => ({ | ||
Category: category, | ||
Amount: categoryAmounts[category] | ||
})); | ||
|
||
const barWidth: number | undefined = windowWidth && windowWidth < 900 ? undefined : 800; | ||
const barHeight: number | undefined = windowWidth && windowWidth < 900 ? undefined : 400; | ||
|
||
return ( | ||
<div className='mt-8 flex items-center justify-center sm:px-6 lg:px-8'> | ||
<div className='w-full px-4 text-center lg:w-2/3'> | ||
<div className='mb-5'> | ||
<h1 id='budget-analysis' className='my-2 mb-4 text-3xl font-semibold'> | ||
Budget Analysis | ||
</h1> | ||
<p>Gain insights into the allocation of funds across different categories through our Budget Analysis</p> | ||
<div className='my-4 flex flex-col justify-between md:m-8 md:flex-row md:items-center md:justify-between'> | ||
<div className='my-2'> | ||
<p className='text-center sm:text-left'>Expenses</p> | ||
<p className='mt-1 text-center text-xl font-semibold sm:text-left'>${totalAmount.toFixed(2)}</p> | ||
</div> | ||
<div className='space-x-4 md:flex'> | ||
<div className='mx-auto'> | ||
<select | ||
className='m-1 w-full rounded-md border border-gray-600 bg-white p-2 text-xs font-semibold text-violet sm:w-auto md:w-48' | ||
value={selectedCategory} | ||
onChange={(e) => setSelectedCategory(e.target.value)} | ||
> | ||
<option value='All Categories'>All Categories</option> | ||
{categories.map((category) => ( | ||
<option key={category} value={category}> | ||
{category} | ||
</option> | ||
))} | ||
</select> | ||
<select | ||
className='m-1 w-full rounded-md border border-gray-600 bg-violet p-2 pr-8 text-xs font-semibold text-white sm:w-auto md:w-48' | ||
value={selectedMonth} | ||
onChange={(e) => setSelectedMonth(e.target.value)} | ||
> | ||
<option value='All Months'>All Months</option> | ||
{months.map((month) => ( | ||
<option key={month} value={month}> | ||
{month} | ||
</option> | ||
))} | ||
</select> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div className='flex justify-center'> | ||
<BarChart width={barWidth} height={barHeight} data={chartData}> | ||
<CartesianGrid strokeDasharray='3 3' /> | ||
<YAxis tickFormatter={(value) => `$${value}`} /> | ||
<Tooltip content={<CustomTooltip />} /> | ||
<Legend /> | ||
<Bar | ||
dataKey='Amount' | ||
fill='#7B5DD3FF' | ||
onClick={(data) => { | ||
const category = data.payload.Category; | ||
const matchedLinkObject: ExpensesLinkItem | undefined = ExpensesLinkData.find((obj) => obj.category === category); | ||
|
||
if (matchedLinkObject) { | ||
window.open(matchedLinkObject.link, '_blank'); | ||
} | ||
}} | ||
/> | ||
</BarChart> | ||
</div> | ||
{windowWidth && windowWidth < 900 ? <ExpensesCard /> : null} | ||
</div> | ||
</div> | ||
); | ||
} |
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,41 @@ | ||
import type { ExpenseItem, Expenses } from '@/types/FinancialSummary/BarChartComponent'; | ||
|
||
import ExpensesLinkData from '../../config/finance/json-data/2024/ExpensesLink.json'; | ||
|
||
/** | ||
* @description Card component displays expense details for a specific month. | ||
* @param {Object} props - Props for Card component. | ||
* @param {string} props.month - Month for which expenses are displayed. | ||
* @param {ExpenseItem[]} props.data - Expense data for the month. | ||
*/ | ||
export default function Card({ month, data }: { month: keyof Expenses; data: ExpenseItem[] }) { | ||
/** | ||
* Handles the click event on an expense category. | ||
* Opens a new window with the corresponding link if available. | ||
* @param {string} category - The expense category clicked. | ||
* {void} | ||
*/ | ||
function handleExpenseClick(category: string) { | ||
const matchedLinkObject = ExpensesLinkData.find((obj) => obj.category === category); | ||
|
||
if (matchedLinkObject) { | ||
window.open(matchedLinkObject.link, '_blank'); | ||
} | ||
} | ||
|
||
return ( | ||
<div className='flex h-56 flex-col overflow-hidden rounded-lg bg-slate-100 p-4 shadow-lg'> | ||
<div className='mb-4 text-lg font-semibold'>{month}</div> | ||
<div className='flex flex-col overflow-auto'> | ||
{data.map((item, index) => ( | ||
<div key={index} className='flex justify-between'> | ||
<div className='m-2 text-sm' onClick={() => handleExpenseClick(item.Category)}> | ||
{item.Category} | ||
</div> | ||
<div className='m-2 text-sm'>${item.Amount}</div> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} |
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,42 @@ | ||
import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; | ||
import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; | ||
|
||
import Button from '../buttons/Button'; | ||
import Heading from '../typography/Heading'; | ||
import Paragraph from '../typography/Paragraph'; | ||
import TextLink from '../typography/TextLink'; | ||
|
||
/** | ||
* @description ContactUs component displays information about getting in touch. | ||
*/ | ||
export default function ContactUs() { | ||
return ( | ||
<div className='px-4 sm:px-6 lg:px-8'> | ||
<div className='my-4 grid lg:grid-cols-9 lg:gap-8 lg:text-center'> | ||
<div className='col-span-5 col-start-3'> | ||
<div className='mx-2'> | ||
<Heading level={HeadingLevel.h1} typeStyle={HeadingTypeStyle.lg}> | ||
Interested in getting in touch? | ||
</Heading> | ||
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-2 max-w-4xl'> | ||
Feel free to contact us if you need more explanation. We are happy to hop on a call and help with | ||
onboarding to the project as a sponsor. Write email to{' '} | ||
<span> | ||
<TextLink | ||
className='text-base font-semibold text-violet' | ||
href='mailto:[email protected]' | ||
target='_blank' | ||
> | ||
[email protected] | ||
</TextLink> | ||
</span> | ||
</Paragraph> | ||
</div> | ||
</div> | ||
</div> | ||
<div className='flex justify-center'> | ||
<Button text='Contact Us' href='mailto:[email protected]' target='_blank' /> | ||
</div> | ||
</div> | ||
); | ||
} |
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,25 @@ | ||
import type { CustomTooltipProps } from '@/types/FinancialSummary/BarChartComponent'; | ||
|
||
/** | ||
* @description CustomTooltip component displays custom tooltip for BarChart. | ||
* @param {boolean} props.active - The active state of the tooltip | ||
* @param {TooltipPayload[]} props.payload - The payload of the tooltip | ||
*/ | ||
export default function CustomTooltip({ active, payload }: CustomTooltipProps) { | ||
/** | ||
* If the tooltip is active and the payload has data, display the category and amount of the bar | ||
*/ | ||
if (active && payload && payload.length) { | ||
const data = payload[0].payload; | ||
|
||
return ( | ||
<div className='bg-opacity-90/90 rounded-md border border-gray-300 bg-white p-2 shadow-md'> | ||
<p className='text-14 mb-1 font-bold'>{data.Category}</p> | ||
<p className='text-12 text-gray-900'>${data.Amount.toFixed(2)}</p> | ||
<p>Click the bar to learn more</p> | ||
</div> | ||
); | ||
} | ||
|
||
return null; | ||
} |
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,51 @@ | ||
import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; | ||
|
||
import { expenseData } from '../data/ExpenseBreakdownData'; | ||
import Heading from '../typography/Heading'; | ||
import Paragraph from '../typography/Paragraph'; | ||
|
||
/** | ||
* @description ExpenseBreakdown component displays a breakdown of expenses. | ||
*/ | ||
export default function ExpenseBreakdown() { | ||
return ( | ||
<div className='bg-[#EFFAFE] px-4 sm:px-6 lg:px-8'> | ||
<div className='mb-16 grid lg:grid-cols-9 lg:gap-8 lg:text-center'> | ||
<div className='col-span-7 col-start-2 my-12'> | ||
<div className='mx-2'> | ||
<Heading className='m-3 text-center text-base'>Expense Breakdown</Heading> | ||
<Paragraph | ||
typeStyle={ParagraphTypeStyle.md} | ||
className='mx-auto my-3 max-w-4xl text-center text-darkGunMetal' | ||
> | ||
Funds from GitHub Sponsors are directly transferred to our AsyncAPI Open Collective account. We maintain | ||
transparency in all expenses, and the TSC approves anticipated expenses. | ||
</Paragraph> | ||
</div> | ||
<div className='mx-3 mt-8 grid grid-cols-1 gap-10 md:grid-cols-3'> | ||
{expenseData.map((expense, index) => ( | ||
<div | ||
key={index} | ||
className='flex flex-col items-center rounded-md bg-white p-4 shadow-md transition-transform hover:scale-105 active:scale-90' | ||
> | ||
<a href={expense.link} target='_blank' rel='noopener noreferrer'> | ||
<div className='text-darkGunMetal'> | ||
<div className='flex flex-col items-center'> | ||
<img | ||
src={expense.imageUrl} | ||
alt={expense.title} | ||
className='m-1 h-auto w-1/5 rounded-md object-cover' | ||
/> | ||
<h2 className='my-2 text-center text-2xl font-semibold text-darkGunMetal'>{expense.title}</h2> | ||
</div> | ||
<p className='text-center text-base text-darkGunMetal'>{expense.description}</p> | ||
</div> | ||
</a> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
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,19 @@ | ||
import type { Expenses } from '@/types/FinancialSummary/BarChartComponent'; | ||
|
||
import ExpensesData from '../../config/finance/json-data/2024/Expenses.json'; | ||
import Card from './Card'; | ||
|
||
/** | ||
* @description ExpensesCard component displays all expenses for each month. | ||
*/ | ||
export default function ExpensesCard() { | ||
return ( | ||
<div className='overflow-x-auto'> | ||
<div className='grid auto-cols-max grid-flow-col gap-4 p-4'> | ||
{Object.entries(ExpensesData).map(function ([month, data], index) { | ||
return <Card key={index} month={month as keyof Expenses} data={data} />; | ||
})} | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.