diff --git a/shared/locales/de/website-finances.json b/shared/locales/de/website-finances.json index bcb9c9eb9..f6880a607 100644 --- a/shared/locales/de/website-finances.json +++ b/shared/locales/de/website-finances.json @@ -19,7 +19,7 @@ "individuals": "{{ count }} Individuen", "institutions": "{{ count }} Institutionen", "past-payouts": "Vergangene Auszahlungen: {{ value, currency }}", - "future-payouts": "Künftige Auszahlungen: {{ value, currency }}", + "payments-future": "Künftige Auszahlungen: {{ value, currency }}", "past-costs": "Bereits verwendet um Kosten zu decken: {{ value, currency }}", "future-costs": "Um kommende Kosten zu decken: {{ value, currency }}" }, @@ -36,16 +36,18 @@ "expenses": "Ausgaben", "payments-total": "{{ value, currency }} ausbezahlt an {{ recipientsCount }} Empfänger:innen", "payments-last-month": "Letzter Monat: {{ value, currency }} an {{ recipientsCount }} Empfänger:innen", - "future-payouts": "Künftige Auszahlungen: {{ value, currency }}", - "total-costs": "{{ value, currency }} Gebühren und Kosten", - "payment-fees": "Zustellgebühren: {{ value, currency }}", - "payment-fees-tooltip": "Dies beinhaltet die Transaktionsgebühren von Zahlungsportalen wie Stripe.", - "transaction-fees": "Transaktionsgebühren: {{ value, currency }}", - "transaction-fees-tooltip": "Dies beinhaltet Kontogebühren, Geldwechselgebühren, sowie Währungsverluste.", - "operating-costs": "Operative Kosten: {{ value, currency }}", - "operating-costs-tooltip": "Dies beinhaltet Kosten für Saläre, Marketing, Fundraising.", - "other-costs": "Andere Kosten: {{ value, currency }}", - "other-costs-tooltip": "??", + "payments-future": "Künftige Auszahlungen: {{ value, currency }}", + "project-costs": "{{ value, currency }} Projektführungskosten", + "transaction-costs": "Zustellgebühren: {{ value, currency }}", + "transaction-costs-tooltip": "Kosten, die anfallen beim Geldtransfer von dir zu uns.", + "delivery-costs": "Auslieferungskosten: {{ value, currency }}", + "delivery-costs-tooltip": "Kosten, die anfallen beim Geldtransfer von uns zu den Empfänger:innen.", + "administrative-costs": "Administrative Kosten: {{ value, currency }}", + "administrative-costs-tooltip": "Kosten für IT Lizenzen, Hosting der Webseite, etc.", + "fundraising-costs": "Fundraising & Marketing: {{ value, currency }}", + "fundraising-costs-tooltip": "Kosten, die für Werbung und Fundraising anfallen.", + "staff-costs": "Lokales Personal: {{ value, currency }}", + "staff-costs-tooltip": "Lohnkosten für Personal in Sierra Leone.", "reserves": "Reserven", "covers-payments": "Reserven decken Auszahlungen für", "covers-payments-1": "{{ recipientsCount }} Empfänger:innen für {{ monthsCount }} Monate", diff --git a/shared/locales/en/website-finances.json b/shared/locales/en/website-finances.json index 257b49a51..9682e86aa 100644 --- a/shared/locales/en/website-finances.json +++ b/shared/locales/en/website-finances.json @@ -7,7 +7,7 @@ "title-1": "Transparency builds trust.", "title-2": "Trust builds solidarity.", "since-march-2020": "Since March 2020", - "totalContributions": "{{ contributorCount }} contributors donated a total of {{value, currency}}", + "totalContributions": "{{ contributorCount }} contributors donated a total of {{ value, currency }}", "totalPayments": "{{ value }} mobile money payments made", "totalRecipients": "{{ value }} recipients enrolled for a 3-year program" }, @@ -19,7 +19,7 @@ "individuals": "{{ count }} Individuals", "institutions": "{{ count }} Institutions", "past-payouts": "Already paid out: {{ value, currency }}", - "future-payouts": "To be paid out: {{ value, currency }}", + "payments-future": "To be paid out: {{ value, currency }}", "past-costs": "Already used to cover costs: {{ value, currency }}", "future-costs": "To be used to cover costs: {{ value, currency }}" }, @@ -36,16 +36,18 @@ "expenses": "Expenses", "payments-total": "{{ value, currency }} paid out to {{ recipientsCount }} recipients", "payments-last-month": "Last month: {{ value, currency }} to {{ recipientsCount }} recipients", - "future-payouts": "To be paid out: {{ value, currency }}", - "total-costs": "{{ value, currency }} fees and costs", - "payment-fees": "Payment fees: {{ value, currency }}", - "payment-fees-tooltip": "This includes transaction fees charged by payment providers like Stripe.", - "transaction-fees": "Transaction fees: {{ value, currency }}", - "transaction-fees-tooltip": "This includes bank account fees, exchange rate fees, or foreign currency losses.", - "operating-costs": "Operating costs: {{ value, currency }}", - "operating-costs-tooltip": "This includes salaries, fundraising, and other costs to run the organization.", - "other-costs": "Other costs: {{ value, currency }}", - "other-costs-tooltip": "??", + "payments-future": "To be paid out: {{ value, currency }}", + "project-costs": "{{ value, currency }} Project Management Costs", + "transaction-costs": "Transaction Costs: {{ value, currency }}", + "transaction-costs-tooltip": "Costs incurred during the transfer of money from you to us.", + "delivery-costs": "Delivery Costs: {{ value, currency }}", + "delivery-costs-tooltip": "Costs incurred during the transfer of money from us to the recipients.", + "administrative-costs": "Administrative Costs: {{ value, currency }}", + "administrative-costs-tooltip": "Costs for IT licenses, website hosting, etc.", + "fundraising-costs": "Fundraising & Marketing: {{ value, currency }}", + "fundraising-costs-tooltip": "Costs incurred for advertising and fundraising.", + "staff-costs": "Local Staff: {{ value, currency }}", + "staff-costs-tooltip": "Salary costs for staff in Sierra Leone.", "reserves": "Reserves", "covers-payments": "This covers payments for", "covers-payments-1": "{{ recipientsCount }} recipients for {{ monthsCount }} months", diff --git a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/info-card.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/info-card.tsx index 39f3ca260..94475e540 100644 --- a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/info-card.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/info-card.tsx @@ -24,14 +24,14 @@ export function InfoCard({ return ( -
+
{sectionTitle} {title} {text}
-
+
{!_.isNil(firstIcon) && !_.isNil(firstContent) && (
{firstIcon}
diff --git a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/page.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/page.tsx index a6bf60fe1..5a03648f7 100644 --- a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/page.tsx @@ -27,6 +27,13 @@ export type SectionProps = { params: DefaultParams & { currency: string }; contributionStats: ContributionStats; paymentStats: PaymentStats; + costs: { + transaction: number; + delivery: number; + administrative: number; + fundraising: number; + staff: number; + }; }; export default async function Page({ params }: TransparencyPageProps) { @@ -37,17 +44,24 @@ export default async function Page({ params }: TransparencyPageProps) { const paymentStats = paymentCalculator.allStats(); return { contributionStats, paymentStats }; }; - const currency = params.currency.toUpperCase() as WebsiteCurrency; const { contributionStats, paymentStats } = await getStats(currency); + // TODO: Calculate these costs dynamically + const costs = { + transaction: 8800, + delivery: 5700, // "Total operative expenses" + administrative: 5600, // "Other project costs" + fundraising: 4500, + staff: 9600, + }; return (
- - - - + + + +
); } diff --git a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1.tsx index 7d13c1e42..7488a6ea6 100644 --- a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1.tsx @@ -3,6 +3,8 @@ import { Card, CardContent, Typography } from '@socialincome/ui'; import _ from 'lodash'; import { SectionProps } from './page'; +export const roundAmount = (amount: number) => Math.round(amount / 10) * 10; + export async function Section1({ params, paymentStats, contributionStats }: SectionProps) { const translator = await Translator.getInstance({ language: params.lang, namespaces: ['website-finances'] }); @@ -18,7 +20,7 @@ export async function Section1({ params, paymentStats, contributionStats }: Sect translator.t('section-1.totalContributions', { context: { contributorCount: contributionStats.totalContributorsCount, - value: contributionStats.totalContributionsAmount, + value: roundAmount(contributionStats.totalContributionsAmount), currency: params.currency, }, }), diff --git a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-2.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-2.tsx index 4f1cd1c08..a0fb42ab8 100644 --- a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-2.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-2.tsx @@ -1,3 +1,4 @@ +import { roundAmount } from '@/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1'; import { HeartIcon } from '@heroicons/react/24/outline'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { Badge, Typography } from '@socialincome/ui'; @@ -5,15 +6,9 @@ import _ from 'lodash'; import { InfoCard } from './info-card'; import { SectionProps } from './page'; -export async function Section2({ params, contributionStats, paymentStats }: SectionProps) { +export async function Section2({ params, contributionStats, paymentStats, costs }: SectionProps) { const translator = await Translator.getInstance({ language: params.lang, namespaces: ['website-finances'] }); - const paymentFees = _.sumBy(contributionStats.totalPaymentFeesByIsInstitution, 'amount'); - - // TODO: Calculate these costs dynamically - const transactionFees = 7800; // "Exchange rate and currency losses" + "Account fees" + "Transaction fees" (without stripe fees) - const operatingCosts = 9300; // "Total operative expenses" - const otherCosts = 9600; // "Other project costs" - const totalCosts = paymentFees + transactionFees + operatingCosts + otherCosts; + const expensesProject = _.sum(Object.values(costs)); return (
@@ -23,7 +18,7 @@ export async function Section2({ params, contributionStats, paymentStats }: Sect } @@ -32,7 +27,10 @@ export async function Section2({ params, contributionStats, paymentStats }: Sect
{translator.t('section-2.contributions-from', { - context: { value: contributionStats.totalIndividualContributionsAmount, currency: params.currency }, + context: { + value: roundAmount(contributionStats.totalIndividualContributionsAmount), + currency: params.currency, + }, })} @@ -44,15 +42,17 @@ export async function Section2({ params, contributionStats, paymentStats }: Sect {translator.t('section-2.past-payouts', { context: { - value: paymentStats.totalPaymentsAmount, + value: roundAmount(paymentStats.totalPaymentsAmount), currency: params.currency, }, })} - {translator.t('section-2.future-payouts', { + {translator.t('section-2.payments-future', { context: { - value: contributionStats.totalIndividualContributionsAmount - paymentStats.totalPaymentsAmount, + value: roundAmount( + contributionStats.totalIndividualContributionsAmount - paymentStats.totalPaymentsAmount, + ), currency: params.currency, }, })} @@ -66,7 +66,7 @@ export async function Section2({ params, contributionStats, paymentStats }: Sect {translator.t('section-2.contributions-from', { context: { - value: contributionStats.totalInstitutionalContributionsAmount, + value: roundAmount(contributionStats.totalInstitutionalContributionsAmount), currency: params.currency, }, })} @@ -80,7 +80,7 @@ export async function Section2({ params, contributionStats, paymentStats }: Sect {translator.t('section-2.past-costs', { context: { - value: totalCosts, + value: roundAmount(expensesProject), currency: params.currency, }, })} @@ -88,7 +88,7 @@ export async function Section2({ params, contributionStats, paymentStats }: Sect {translator.t('section-2.future-costs', { context: { - value: contributionStats.totalInstitutionalContributionsAmount - totalCosts, + value: roundAmount(contributionStats.totalInstitutionalContributionsAmount - expensesProject), currency: params.currency, }, })} diff --git a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3.tsx index b3049f7a0..e83b7d025 100644 --- a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3.tsx @@ -1,3 +1,4 @@ +import { roundAmount } from '@/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { Typography } from '@socialincome/ui'; import { SectionProps } from './page'; @@ -19,7 +20,7 @@ export async function Section3({ params, contributionStats }: SectionProps) { {translator.t('section-3.title')} - + {translator.t('section-3.subtitle', { context: { value: totalContributionsByCountry.length } })} @@ -32,7 +33,11 @@ export async function Section3({ params, contributionStats }: SectionProps) { translations={{ country: translator.t(entry.country), total: translator.t('section-3.country-amount', { - context: { value: entry.amount, currency: params.currency, contributorsCount: entry.usersCount }, + context: { + value: roundAmount(entry.amount), + currency: params.currency, + contributorsCount: entry.usersCount, + }, }), }} /> diff --git a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-4.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-4.tsx index 89f0e4880..dbb8caf85 100644 --- a/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-4.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-4.tsx @@ -8,18 +8,12 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typography } import _ from 'lodash'; import { InfoCard } from './info-card'; import { SectionProps } from './page'; +import { roundAmount } from './section-1'; -export async function Section4({ params, paymentStats, contributionStats }: SectionProps) { +export async function Section4({ params, paymentStats, contributionStats, costs }: SectionProps) { const translator = await Translator.getInstance({ language: params.lang, namespaces: ['website-finances'] }); - const paymentFees = _.sumBy(contributionStats.totalPaymentFeesByIsInstitution, 'amount'); - - // TODO: Calculate these costs dynamically - const transactionFees = 7800; // "Exchange rate and currency losses" + "Account fees" + "Transaction fees" (without stripe fees) - const operatingCosts = 9300; // "Total operative expenses" - const otherCosts = 9600; // "Other project costs" - - const totalExpenses = paymentFees + transactionFees + operatingCosts + otherCosts + paymentStats.totalPaymentsAmount; - const totalReserves = contributionStats.totalContributionsAmount - totalExpenses; + const expensesTotal = _.sum(Object.values(costs)) + paymentStats.totalPaymentsAmount; + const reservesTotal = contributionStats.totalContributionsAmount - expensesTotal; const exchangeRateSLE = await getLatestExchangeRate(firestoreAdmin, 'SLE'); return ( @@ -28,13 +22,16 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect {translator.t('section-4.title')} - - {translator.t('section-4.subtitle')} - + {translator.t('section-4.subtitle')}
} firstContent={ @@ -43,7 +40,7 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect {translator.t('section-4.payments-total', { context: { - value: paymentStats.totalPaymentsAmount, + value: roundAmount(paymentStats.totalPaymentsAmount), currency: params.currency, recipientsCount: paymentStats.totalRecipientsCount, }, @@ -53,16 +50,18 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect {translator.t('section-4.payments-last-month', { context: { - value: _.last(paymentStats.totalPaymentsByMonth)?.amount, + value: roundAmount(_.last(paymentStats.totalPaymentsByMonth)?.amount as number), currency: params.currency, recipientsCount: _.last(paymentStats.totalPaymentsByMonth)?.recipientsCount, }, })} - {translator.t('section-4.future-payouts', { + {translator.t('section-4.payments-future', { context: { - value: contributionStats.totalIndividualContributionsAmount - paymentStats.totalPaymentsAmount, + value: roundAmount( + contributionStats.totalIndividualContributionsAmount - paymentStats.totalPaymentsAmount, + ), currency: params.currency, }, })} @@ -74,18 +73,15 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect
- {translator.t('section-4.total-costs', { - context: { - value: paymentFees + transactionFees + operatingCosts + otherCosts, - currency: params.currency, - }, + {translator.t('section-4.project-costs', { + context: { value: roundAmount(_.sum(Object.values(costs))), currency: params.currency }, })}
- {translator.t('section-4.payment-fees', { + {translator.t('section-4.transaction-costs', { context: { - value: paymentFees, + value: roundAmount(costs.transaction), currency: params.currency, }, })} @@ -94,61 +90,59 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect - {/*{translator.t('section-4.payment-fees-tooltip')}*/} - Stripe fees + {translator.t('section-4.transaction-costs-tooltip')} - {translator.t('section-4.transaction-fees', { - context: { - value: transactionFees, - currency: params.currency, - }, + {translator.t('section-4.delivery-costs', { + context: { value: roundAmount(costs.delivery), currency: params.currency }, })} - {/*{translator.t('section-4.transaction-fees-tooltip')}*/} - - {'"Exchange rate and currency losses" + "Account fees" + "Transaction fees" (without Stripe fees)'} - + {translator.t('section-4.delivery-costs-tooltip')} - {translator.t('section-4.operating-costs', { - context: { - value: operatingCosts, - currency: params.currency, - }, + {translator.t('section-4.administrative-costs', { + context: { value: roundAmount(costs.administrative), currency: params.currency }, })} - {/*{translator.t('section-4.operating-costs-tooltip')}*/} - {'"Total operative expenses"'} + {translator.t('section-4.administrative-costs-tooltip')} - {translator.t('section-4.other-costs', { - context: { - value: otherCosts, - currency: params.currency, - }, + {translator.t('section-4.fundraising-costs', { + context: { value: roundAmount(costs.fundraising), currency: params.currency }, + })} + + + + + + {translator.t('section-4.fundraising-costs-tooltip')} + + + + + {translator.t('section-4.staff-costs', { + context: { value: roundAmount(costs.staff), currency: params.currency }, })} - {/*{translator.t('section-4.other-costs-tooltip')}*/} - {'"Other project costs"'} + {translator.t('section-4.staff-costs-tooltip')} @@ -159,7 +153,7 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect } @@ -171,7 +165,7 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect {translator.t('section-4.covers-payments-1', { context: { - recipientsCount: (totalReserves / (PAYMENT_AMOUNT / exchangeRateSLE) / 36).toFixed(0), + recipientsCount: (reservesTotal / (PAYMENT_AMOUNT / exchangeRateSLE) / 36).toFixed(0), monthsCount: 36, }, })} @@ -179,7 +173,7 @@ export async function Section4({ params, paymentStats, contributionStats }: Sect {translator.t('section-4.covers-payments-2', { context: { - recipientsCount: (totalReserves / (PAYMENT_AMOUNT / exchangeRateSLE)).toFixed(0), + recipientsCount: (reservesTotal / (PAYMENT_AMOUNT / exchangeRateSLE)).toFixed(0), }, })} diff --git a/website/src/app/[lang]/[region]/donate/one-time/page.tsx b/website/src/app/[lang]/[region]/donate/one-time/page.tsx index 8bf5a4ea7..04c34bd7f 100644 --- a/website/src/app/[lang]/[region]/donate/one-time/page.tsx +++ b/website/src/app/[lang]/[region]/donate/one-time/page.tsx @@ -8,7 +8,7 @@ export default async function Page({ params: { lang, region } }: DefaultPageProp return (
- + {translator.t('one-time.title')}