192 lines
5.2 KiB
TypeScript

import axios from 'axios'
import { useState, useEffect } from 'react'
import { useAppState } from '#components/AppShell/hooks/useAppState'
import type { Item } from '#types/Item'
interface AccountData {
account: {
name: string
type: string
stats: {
balance: {
valueInCents: number
currency: string
} | null
totalAmountReceived: {
valueInCents: number
currency: string
}
totalAmountSpent: {
valueInCents: number
currency: string
}
contributionsCount: number
contributorsCount: number
}
}
}
interface GraphQLResponse<T> {
data?: T
errors?: { message: string }[]
}
const GET_TRANSACTIONS = `
query GetAccountStats($slug: String!) {
account(slug: $slug) {
name
type
stats {
balance {
valueInCents
currency
}
totalAmountReceived(net: true) {
valueInCents
currency
}
totalAmountSpent {
valueInCents
currency
}
contributionsCount
contributorsCount
}
}
}
`
const formatCurrency = (valueInCents: number, currency: string) => {
const value = valueInCents / 100
const options: Intl.NumberFormatOptions = {
style: 'currency',
currency,
...(Math.abs(value) >= 1000 ? { minimumFractionDigits: 0, maximumFractionDigits: 0 } : {}),
}
return new Intl.NumberFormat('de-DE', options).format(value)
}
export const CrowdfundingView = ({ item }: { item: Item }) => {
// Hier wird slug aus dem Item extrahiert.
const slug = item.openCollectiveSlug
const appState = useAppState()
const token = appState.openCollectiveApiKey
const graphqlClient = axios.create({
baseURL: 'https://api.opencollective.com/graphql/v2',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
})
const [data, setData] = useState<AccountData | null>(null)
const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const fetchData = async () => {
setLoading(true)
setError(null)
try {
const response = await graphqlClient.post<GraphQLResponse<AccountData>>('', {
query: GET_TRANSACTIONS,
variables: { slug },
})
if (response.data.errors?.length) {
setError(response.data.errors[0].message)
} else {
setData(response.data.data ?? null)
}
} catch (err: unknown) {
if (err instanceof Error) {
setError(err.message)
} else {
throw err
}
}
setLoading(false)
}
if (slug) {
void fetchData()
}
}, [slug])
if (!slug) return null
if (loading)
return (
<div className='tw-flex tw-justify-center'>
<span className='tw-loading tw-loading-spinner tw-loading-lg tw-text-neutral-content'></span>
</div>
)
if (error) {
return <p className='tw-text-center tw-text-lg tw-text-red-500'>Error: {error}</p>
}
if (!data?.account) {
return (
<p className='tw-text-center tw-text-lg tw-text-red-500'>
No data available for this account.
</p>
)
}
const { stats } = data.account
const balanceValueInCents = stats.balance?.valueInCents ?? 0
const currency = stats.balance?.currency ?? 'USD'
const currentBalance = balanceValueInCents
return (
<div className='tw-mx-6 tw-mb-6'>
<div className='tw-card tw-bg-base-200 tw-w-fit tw-max-w-full tw-shadow'>
<div className='tw-stats tw-bg-base-200 tw-stats-horizontal tw-rounded-b-none'>
<div className='tw-stat tw-p-3'>
<div className='tw-stat-title'>Current Balance</div>
<div className='tw-stat-value tw-text-xl lg:tw-text-3xl'>
{formatCurrency(currentBalance, currency)}
</div>
</div>
<div className='tw-stat tw-p-3'>
<div className='tw-stat-title'>Received</div>
<div className='tw-stat-value tw-text-green-500 tw-text-xl lg:tw-text-3xl'>
{formatCurrency(stats.totalAmountReceived.valueInCents, currency)}
</div>
</div>
<div className='tw-stat tw-p-3'>
<div className='tw-stat-title'>Spent</div>
<div className='tw-stat-value tw-text-red-500 tw-text-xl lg:tw-text-3xl'>
{formatCurrency(stats.totalAmountReceived.valueInCents - currentBalance, currency)}
</div>
</div>
</div>
<hr></hr>
<div className='tw-m-4 tw-items-center'>
<a href={`https://opencollective.com/${slug}/donate`} target='_blank' rel='noreferrer'>
<button className='tw-btn tw-btn-sm tw-btn-primary tw-float-right tw-ml-4'>
Donate
</button>
</a>
<div className='tw-flex-1 tw-mr-4'>
Support{' '}
<a
className='tw-font-bold'
href={`https://opencollective.com/${slug}`}
target='_blank'
rel='noreferrer'
>
{data.account.name}
</a>{' '}
on <span className='tw-font-bold'>Open&nbsp;Collective</span>
</div>
</div>
</div>
</div>
)
}