Skip to content

Commit

Permalink
Merge branch 'migrate-ts' into ag-ts-tools
Browse files Browse the repository at this point in the history
  • Loading branch information
devilkiller-ag authored Mar 20, 2024
2 parents 1b70f5b + a2e4cce commit 5bbf451
Show file tree
Hide file tree
Showing 30 changed files with 2,267 additions and 608 deletions.
19 changes: 19 additions & 0 deletions components/Caption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';

interface CaptionProps {
children: React.ReactNode;
}

/**
* @description This component displays textual captions.
*
* @param {CaptionProps} props - The props for the Caption component.
* @param {React.ReactNode} props.children - The content to be displayed as the caption.
*/
export default function Caption({ children } : CaptionProps) {
return (
<p className='mt-2 text-center text-xs text-gray-500' data-testid='Caption-paragraph'>
{children}
</p>
);
};
78 changes: 78 additions & 0 deletions components/FinancialSummary/AsyncAPISummary.tsx
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>
);
}
136 changes: 136 additions & 0 deletions components/FinancialSummary/BarChartComponent.tsx
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>
);
}
41 changes: 41 additions & 0 deletions components/FinancialSummary/Card.tsx
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>
);
}
42 changes: 42 additions & 0 deletions components/FinancialSummary/ContactUs.tsx
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>
);
}
25 changes: 25 additions & 0 deletions components/FinancialSummary/CustomTooltip.tsx
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;
}
51 changes: 51 additions & 0 deletions components/FinancialSummary/ExpenseBreakdown.tsx
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>
);
}
Loading

0 comments on commit 5bbf451

Please sign in to comment.